Example #1
0
int ntfs_rename_r (struct _reent *r, const char *oldName, const char *newName)
{
    ntfs_log_trace("oldName %s, newName %s\n", oldName, newName);

    ntfs_vd *vd = NULL;
    ntfs_inode *ni = NULL;

    // Get the volume descriptor for this path
    vd = ntfsGetVolume(oldName);
    if (!vd) {
        r->_errno = ENODEV;
        return -1;
    }

    // Lock
    ntfsLock(vd);

    // You cannot rename between devices
    if(vd != ntfsGetVolume(newName)) {
        ntfsUnlock(vd);
        r->_errno = EXDEV;
        return -1;
    }

    // Check that there is no existing entry with the new name
    ni = ntfsOpenEntry(vd, newName);
    if (ni) {
        ntfsCloseEntry(vd, ni);
        ntfsUnlock(vd);
        r->_errno = EEXIST;
        return -1;
    }

    // Link the old entry with the new one
    if (ntfsLink(vd, oldName, newName)) {
        ntfsUnlock(vd);
        return -1;
    }

    // Unlink the old entry
    if (ntfsUnlink(vd, oldName)) {
        if (ntfsUnlink(vd, newName)) {
            ntfsUnlock(vd);
            return -1;
        }
        ntfsUnlock(vd);
        return -1;
    }

    // Unlock
    ntfsUnlock(vd);

    return 0;
}
Example #2
0
int ntfs_mkdir_r (struct _reent *r, const char *path, int mode)
{
    ntfs_log_trace("path %s, mode %i\n", path, mode);

    ntfs_vd *vd = NULL;
    ntfs_inode *ni = NULL;

    // Get the volume descriptor for this path
    vd = ntfsGetVolume(path);
    if (!vd) {
        r->_errno = ENODEV;
        return -1;
    }

    // Lock
    ntfsLock(vd);

    // Create the directory
    ni = ntfsCreate(vd, path, S_IFDIR, NULL);
    if (!ni) {
        ntfsUnlock(vd);
        r->_errno = errno;
        return -1;
    }

    // Close the directory
    ntfsCloseEntry(vd, ni);

    // Unlock
    ntfsUnlock(vd);

    return 0;
}
Example #3
0
int ntfs_link_r (struct _reent *r, const char *existing, const char *newLink)
{
    ntfs_log_trace("existing %s, newLink %s\n", existing, newLink);

    ntfs_vd *vd = NULL;
    ntfs_inode *ni = NULL;

    // Get the volume descriptor for this path
    vd = ntfsGetVolume(existing);
    if (!vd) {
        r->_errno = ENODEV;
        return -1;
    }

    // Lock
    ntfsLock(vd);

    // Create a symbolic link between the two paths
    ni = ntfsCreate(vd, existing, S_IFLNK, newLink);
    if (!ni) {
        ntfsUnlock(vd);
        r->_errno = errno;
        return -1;
    }

    // Close the symbolic link
    ntfsCloseEntry(vd, ni);

    // Unlock
    ntfsUnlock(vd);

    return 0;
}
Example #4
0
int ntfs_unlink_r (struct _reent *r, const char *name)
{
    ntfs_log_trace("name %s\n", name);

    // Unlink the entry
    int ret = ntfsUnlink(ntfsGetVolume(name), name);
    if (ret)
        r->_errno = errno;

    return ret;
}
Example #5
0
int ntfs_stat_r (struct _reent *r, const char *path, struct stat *st)
{
    // Short circuit cases were we don't actually have to do anything
    if (!st || !path)
        return 0;

    ntfs_log_trace("path %s, st %p\n", path, st);

    ntfs_vd *vd = NULL;
    ntfs_inode *ni = NULL;

    // Get the volume descriptor for this path
    vd = ntfsGetVolume(path);
    if (!vd) {
        r->_errno = ENODEV;
        return -1;
    }

    if(strcmp(path, ".") == 0 || strcmp(path, "..") == 0)
    {
        memset(st, 0, sizeof(struct stat));
        st->st_mode = S_IFDIR;
        return 0;
    }

    // Lock
    ntfsLock(vd);

    // Find the entry
    ni = ntfsOpenEntry(vd, path);
    if (!ni) {
        r->_errno = errno;
        ntfsUnlock(vd);
        return -1;
    }

    // Get the entry stats
    int ret = ntfsStat(vd, ni, st);
    if (ret)
        r->_errno = errno;

    // Close the entry
    ntfsCloseEntry(vd, ni);

    ntfsUnlock(vd);

    return 0;
}
Example #6
0
int ntfs_chdir_r (struct _reent *r, const char *name)
{
    ntfs_log_trace("name %s\n", name);

    ntfs_vd *vd = NULL;
    ntfs_inode *ni = NULL;

    // Get the volume descriptor for this path
    vd = ntfsGetVolume(name);
    if (!vd) {
        r->_errno = ENODEV;
        return -1;
    }

    // Lock
    ntfsLock(vd);

    // Find the directory
    ni = ntfsOpenEntry(vd, name);
    if (!ni) {
        ntfsUnlock(vd);
        r->_errno = ENOENT;
        return -1;
    }

    // Ensure that this directory is indeed a directory
    if (!(ni->mrec->flags && MFT_RECORD_IS_DIRECTORY)) {
        ntfsCloseEntry(vd, ni);
        ntfsUnlock(vd);
        r->_errno = ENOTDIR;
        return -1;
    }

    // Close the old current directory (if any)
    if (vd->cwd_ni)
        ntfsCloseEntry(vd, vd->cwd_ni);

    // Set the new current directory
    vd->cwd_ni = ni;

    // Unlock
    ntfsUnlock(vd);

    return 0;
}
Example #7
0
const char *ntfsGetVolumeName (const char *name)
{
    ntfs_vd *vd = NULL;

    // Sanity check
    if (!name) {
        errno = EINVAL;
        return NULL;
    }

    // Get the devices volume descriptor
    vd = ntfsGetVolume(name);
    if (!vd) {
        errno = ENODEV;
        return NULL;
    }
    return vd->vol->vol_name;
}
Example #8
0
void ntfsUnmount (const char *name, bool force)
{
    ntfs_vd *vd = NULL;

    // Get the devices volume descriptor
    vd = ntfsGetVolume(name);
    if (!vd)
        return;

    // Remove the device from the devoptab table
    ntfsRemoveDevice(name);

    // Deinitialise the volume descriptor
    ntfsDeinitVolume(vd);

    // Unmount the volume
    ntfs_umount(vd->vol, force);

    // Free the volume descriptor
    ntfs_free(vd);

    return;
}
Example #9
0
int ntfs_open_r(struct _reent *r, void *fileStruct, const char *path, int flags, int mode)
{
	ntfs_log_trace("fileStruct %p, path %s, flags %i, mode %i\n", (void *) fileStruct, path, flags, mode);

	ntfs_file_state* file = STATE(fileStruct);

	// Get the volume descriptor for this path
	file->vd = ntfsGetVolume(path);
	if (!file->vd) {
		r->_errno = ENODEV;
		return -1;
	}

	// Lock
	ntfsLock(file->vd);


	// Determine which mode the file is opened for
	file->flags = flags;
	if ((flags & 0x03) == O_RDONLY) {
		file->read = true;
		file->write = false;
		file->append = false;
	} else if ((flags & 0x03) == O_WRONLY) {
		file->read = false;
		file->write = true;
		file->append = (flags & O_APPEND);
	} else if ((flags & 0x03) == O_RDWR) {
		file->read = true;
		file->write = true;
		file->append = (flags & O_APPEND);
	} else {
		r->_errno = EACCES;
		ntfsUnlock(file->vd);
		return -1;
	}

	// Try and find the file and (if found) ensure that it is not a directory
	file->ni = ntfsOpenEntry(file->vd, path);
	if (file->ni && (file->ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)) {
		ntfsCloseEntry(file->vd, file->ni);
		ntfsUnlock(file->vd);
		r->_errno = EISDIR;
		return -1;
	}

	// Are we creating this file?
	if ((flags & O_CREAT) && !file->ni) {

		// Create the file
		file->ni = ntfsCreate(file->vd, path, S_IFREG, NULL);
		if (!file->ni) {
			ntfsUnlock(file->vd);
			return -1;
		}

	}

	// Sanity check, the file should be open by now
	if (!file->ni) {
		ntfsUnlock(file->vd);
		r->_errno = ENOENT;
		return -1;
	}

	// Open the files data attribute
	file->data_na = ntfs_attr_open(file->ni, AT_DATA, AT_UNNAMED, 0);
	if (!file->data_na) {
		ntfsCloseEntry(file->vd, file->ni);
		ntfsUnlock(file->vd);
		return -1;
	}

	// Determine if this files data is compressed and/or encrypted
	file->compressed = NAttrCompressed(file->data_na) || (file->ni->flags & FILE_ATTR_COMPRESSED);
	file->encrypted = NAttrEncrypted(file->data_na) || (file->ni->flags & FILE_ATTR_ENCRYPTED);

	// We cannot read/write encrypted files
	if (file->encrypted) {
		ntfs_attr_close(file->data_na);
		ntfsCloseEntry(file->vd, file->ni);
		ntfsUnlock(file->vd);
		r->_errno = EACCES;
		return -1;
	}

	// Make sure we aren't trying to write to a read-only file
	if ((file->ni->flags & FILE_ATTR_READONLY) && file->write) {
		ntfs_attr_close(file->data_na);
		ntfsCloseEntry(file->vd, file->ni);
		ntfsUnlock(file->vd);
		r->_errno = EROFS;
		return -1;
	}

	// Truncate the file if requested
	if ((flags & O_TRUNC) && file->write) {
		if (ntfs_attr_truncate(file->data_na, 0)) {
			ntfs_attr_close(file->data_na);
			ntfsCloseEntry(file->vd, file->ni);
			ntfsUnlock(file->vd);
			r->_errno = errno;
			return -1;
		}
	}

	// Set the files current position and length
	file->pos = 0;
	file->len = file->data_na->data_size;

	ntfs_log_trace("file->len %llu\n", file->len);

	// Update file times
	ntfsUpdateTimes(file->vd, file->ni, NTFS_UPDATE_ATIME);

	// Insert the file into the double-linked FILO list of open files
	if (file->vd->firstOpenFile) {
		file->nextOpenFile = file->vd->firstOpenFile;
		file->vd->firstOpenFile->prevOpenFile = file;
	} else {
		file->nextOpenFile = NULL;
	}
	file->prevOpenFile = NULL;
	file->vd->firstOpenFile = file;
	file->vd->openFileCount++;

	file->is_ntfs = 1;
	// Unlock
	ntfsUnlock(file->vd);

	return (int)(intptr_t)fileStruct;
	//return (int)(s64) fileStruct;
}
Example #10
0
int ntfs_file_to_sectors(struct _reent *r, const char *path, uint32_t *sec_out, uint32_t *size_out, int max, int phys)
{
	ntfs_file_state fileStruct;
	ntfs_file_state* file = &fileStruct;
	uint32_t s_count = 0;
	//size_t len;
	off64_t len;

	// Get the volume descriptor for this path
	file->vd = ntfsGetVolume(path);
	if (!file->vd) {
		r->_errno = ENODEV;
		return -1;
	}

	// Lock
	ntfsLock(file->vd);

	// Try and find the file and (if found) ensure that it is not a directory
	file->ni = ntfsOpenEntry(file->vd, path);
	if (file->ni && (file->ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)) {
		ntfsCloseEntry(file->vd, file->ni);
		ntfsUnlock(file->vd);
		r->_errno = EISDIR;
		return -1;
	}

	// Sanity check, the file should be open by now
	if (!file->ni) {
		ntfsUnlock(file->vd);
		r->_errno = ENOENT;
		return -1;
	}

	// Open the files data attribute
	file->data_na = ntfs_attr_open(file->ni, AT_DATA, AT_UNNAMED, 0);
	if(!file->data_na) {
		ntfsCloseEntry(file->vd, file->ni);
		ntfsUnlock(file->vd);
		return -1;
	}

	// Determine if this files data is compressed and/or encrypted
	file->compressed = NAttrCompressed(file->data_na) || (file->ni->flags & FILE_ATTR_COMPRESSED);
	file->encrypted = NAttrEncrypted(file->data_na) || (file->ni->flags & FILE_ATTR_ENCRYPTED);

	// We cannot read/write encrypted files
	if (file->encrypted) {
		ntfs_attr_close(file->data_na);
		ntfsCloseEntry(file->vd, file->ni);
		ntfsUnlock(file->vd);
		r->_errno = EACCES;
		return -1;
	}

	// Set the files current position and length
	file->pos = 0;
	len = file->len = file->data_na->data_size;

	struct ntfs_device *dev = file->vd->dev;
	gekko_fd *fd = DEV_FD(dev);

	while (len && s_count < max) {
		//size_t ret = ntfs_attr_to_sectors(file->data_na, file->pos, len, sec_out, size_out, max, &s_count, (u32) fd->sectorSize);
		off64_t ret = ntfs_attr_to_sectors(file->data_na, file->pos, len, sec_out, size_out, max, &s_count, (u32) fd->sectorSize);
		if (ret <= 0 || ret > len) {
			ntfsUnlock(file->vd);
			r->_errno = errno;
			return -1;
		}
		len -= ret;
		file->pos += ret;
	}

	if (phys)
	{
		uint32_t i;

		for (i = 0; i < s_count; i++)
		{
			sec_out[i] += fd->startSector;
		}
	}

	ntfs_attr_close(file->data_na);
	ntfsCloseEntry(file->vd, file->ni);
	// Unlock
	ntfsUnlock(file->vd);

	return s_count;
}
Example #11
0
DIR_ITER *ntfs_diropen_r (struct _reent *r, DIR_ITER *dirState, const char *path)
{
    ntfs_log_trace("dirState %p, path %s\n", dirState, path);

    ntfs_dir_state* dir = STATE(dirState);
    s64 position = 0;

    // Get the volume descriptor for this path
    dir->vd = ntfsGetVolume(path);
    if (!dir->vd) {
        r->_errno = ENODEV;
        return NULL;
    }

    // Lock
    ntfsLock(dir->vd);

    // Find the directory
    dir->ni = ntfsOpenEntry(dir->vd, path);
    if (!dir->ni) {
        ntfsUnlock(dir->vd);
        r->_errno = ENOENT;
        return NULL;
    }

    // Ensure that this directory is indeed a directory
    if (!(dir->ni->mrec->flags && MFT_RECORD_IS_DIRECTORY)) {
        ntfsCloseEntry(dir->vd, dir->ni);
        ntfsUnlock(dir->vd);
        r->_errno = ENOTDIR;
        return NULL;
    }

    // Read the directory
    dir->first = dir->current = NULL;
    if (ntfs_readdir(dir->ni, &position, dirState, (ntfs_filldir_t)ntfs_readdir_filler)) {
        ntfsCloseDir(dir);
        ntfsUnlock(dir->vd);
        r->_errno = errno;
        return NULL;
    }

    // Move to the first entry in the directory
    dir->current = dir->first;

    // Update directory times
    ntfsUpdateTimes(dir->vd, dir->ni, NTFS_UPDATE_ATIME);

    // Insert the directory into the double-linked FILO list of open directories
    if (dir->vd->firstOpenDir) {
        dir->nextOpenDir = dir->vd->firstOpenDir;
        dir->vd->firstOpenDir->prevOpenDir = dir;
    } else {
        dir->nextOpenDir = NULL;
    }
    dir->prevOpenDir = NULL;
    dir->vd->cwd_ni = dir->ni;
    dir->vd->firstOpenDir = dir;
    dir->vd->openDirCount++;

    // Unlock
    ntfsUnlock(dir->vd);

    return dirState;
}
Example #12
0
int ntfs_statvfs_r (struct _reent *r, const char *path, struct statvfs *buf)
{
    ntfs_log_trace("path %s, buf %p\n", path, buf);

    ntfs_vd *vd = NULL;
    s64 size;
    int delta_bits;

    // Get the volume descriptor for this path
    vd = ntfsGetVolume(path);
    if (!vd) {
        r->_errno = ENODEV;
        return -1;
    }

    // Short circuit cases were we don't actually have to do anything
    if (!buf)
        return 0;

    // Lock
    ntfsLock(vd);

    // Zero out the stat buffer
    memset(buf, 0, sizeof(struct statvfs));

    if(ntfs_volume_get_free_space(vd->vol) < 0)
    {
        ntfsUnlock(vd);
        return -1;
    }

    // File system block size
    buf->f_bsize = vd->vol->cluster_size;

    // Fundamental file system block size
    buf->f_frsize = vd->vol->cluster_size;

    // Total number of blocks on file system in units of f_frsize
    buf->f_blocks = vd->vol->nr_clusters;

    // Free blocks available for all and for non-privileged processes
    size = MAX(vd->vol->free_clusters, 0);
    buf->f_bfree = buf->f_bavail = size;

    // Free inodes on the free space
    delta_bits = vd->vol->cluster_size_bits - vd->vol->mft_record_size_bits;
    if (delta_bits >= 0)
        size <<= delta_bits;
    else
        size >>= -delta_bits;

    // Number of inodes at this point in time
    buf->f_files = (vd->vol->mftbmp_na->allocated_size << 3) + size;

    // Free inodes available for all and for non-privileged processes
    size += vd->vol->free_mft_records;
    buf->f_ffree = buf->f_favail = MAX(size, 0);

    // File system id
    buf->f_fsid = vd->id;

    // Bit mask of f_flag values.
    buf->f_flag = (NVolReadOnly(vd->vol) ? ST_RDONLY : 0);

    // Maximum length of filenames
    buf->f_namemax = NTFS_MAX_NAME_LEN;

    // Unlock
    ntfsUnlock(vd);

    return 0;
}
Example #13
0
bool ntfsSetVolumeName (const char *name, const char *volumeName)
{
    ntfs_vd *vd = NULL;
    ntfs_attr *na = NULL;
    ntfschar *ulabel = NULL;
    int ulabel_len;

    // Sanity check
    if (!name) {
        errno = EINVAL;
        return false;
    }

    // Get the devices volume descriptor
    vd = ntfsGetVolume(name);
    if (!vd) {
        errno = ENODEV;
        return false;
    }

    // Lock
    ntfsLock(vd);

    // Convert the new volume name to unicode
    ulabel_len = ntfsLocalToUnicode(volumeName, &ulabel) * sizeof(ntfschar);
    if (ulabel_len < 0) {
        ntfsUnlock(vd);
        errno = EINVAL;
        return false;
    }

    // Check if the volume name attribute exists
    na = ntfs_attr_open(vd->vol->vol_ni, AT_VOLUME_NAME, NULL, 0);
    if (na) {

        // It does, resize it to match the length of the new volume name
        if (ntfs_attr_truncate(na, ulabel_len)) {
            ntfs_free(ulabel);
            ntfsUnlock(vd);
            return false;
        }

        // Write the new volume name
        if (ntfs_attr_pwrite(na, 0, ulabel_len, ulabel) != ulabel_len) {
            ntfs_free(ulabel);
            ntfsUnlock(vd);
            return false;
        }

    } else {

        // It doesn't, create it now
        if (ntfs_attr_add(vd->vol->vol_ni, AT_VOLUME_NAME, NULL, 0, (u8*)ulabel, ulabel_len)) {
            ntfs_free(ulabel);
            ntfsUnlock(vd);
            return false;
        }

    }

    // Reset the volumes name cache (as it has now been changed)
    vd->name[0] = '\0';

    // Close the volume name attribute
    if (na)
        ntfs_attr_close(na);

    // Sync the volume node
    if (ntfs_inode_sync(vd->vol->vol_ni)) {
        ntfs_free(ulabel);
        ntfsUnlock(vd);
        return false;
    }

    // Clean up
    ntfs_free(ulabel);

    // Unlock
    ntfsUnlock(vd);

    return true;
}
Example #14
0
const char *ntfsGetVolumeName (const char *name)
{
    ntfs_vd *vd = NULL;
    //ntfs_attr *na = NULL;
    //ntfschar *ulabel = NULL;
    //char *volumeName = NULL;

    // Sanity check
    if (!name) {
        errno = EINVAL;
        return NULL;
    }

    // Get the devices volume descriptor
    vd = ntfsGetVolume(name);
    if (!vd) {
        errno = ENODEV;
        return NULL;
    }
    return vd->vol->vol_name;
    /*

        // If the volume name has already been cached then just use that
        if (vd->name[0])
            return vd->name;

        // Lock
        ntfsLock(vd);

        // Check if the volume name attribute exists
        na = ntfs_attr_open(vd->vol->vol_ni, AT_VOLUME_NAME, NULL, 0);
        if (!na) {
            ntfsUnlock(vd);
            errno = ENOENT;
            return false;
        }

        // Allocate a buffer to store the raw volume name
        ulabel = ntfs_alloc(na->data_size * sizeof(ntfschar));
        if (!ulabel) {
            ntfsUnlock(vd);
            errno = ENOMEM;
            return false;
        }

        // Read the volume name
        if (ntfs_attr_pread(na, 0, na->data_size, ulabel) != na->data_size) {
            ntfs_free(ulabel);
            ntfsUnlock(vd);
            errno = EIO;
            return false;
        }

        // Convert the volume name to the current local
        if (ntfsUnicodeToLocal(ulabel, na->data_size, &volumeName, 0) < 0) {
            errno = EINVAL;
            ntfs_free(ulabel);
            ntfsUnlock(vd);
            return false;
        }

        // If the volume name was read then cache it (for future fetches)
        if (volumeName)
            strcpy(vd->name, volumeName);

        // Close the volume name attribute
        if (na)
            ntfs_attr_close(na);

        // Clean up
        ntfs_free(volumeName);
        ntfs_free(ulabel);

        // Unlock
        ntfsUnlock(vd);

        return vd->name;
    */
}
Example #15
0
int ntfsLink (ntfs_vd *vd, const char *old_path, const char *new_path)
{
    ntfs_inode *dir_ni = NULL, *ni = NULL;
    char *dir = NULL;
    char *name = NULL;
    ntfschar *uname = NULL;
    int uname_len;
    int res = 0;

    // Sanity check
    if (!vd) {
        errno = ENODEV;
        return -1;
    }

    // You cannot link between devices
    if(vd != ntfsGetVolume(new_path)) {
        errno = EXDEV;
        return -1;
    }

    // Get the actual paths of the entry
    old_path = ntfsRealPath(old_path);
    new_path = ntfsRealPath(new_path);
    if (!old_path || !new_path) {
        errno = EINVAL;
        return -1;
    }

    // Lock
    ntfsLock(vd);

    // Get the unicode name for the entry and find its parent directory
    // TODO: This looks horrible, clean it up
    dir = strdup(new_path);
    if (!dir) {
        errno = EINVAL;
        goto cleanup;
    }
    name = strrchr(dir, '/');
    if (name)
        name++;
    else
        name = dir;
    uname_len = ntfsLocalToUnicode(name, &uname);
    if (uname_len < 0) {
        errno = EINVAL;
        goto cleanup;
    }
    *name = 0;

    // Find the entry
    ni = ntfsOpenEntry(vd, old_path);
    if (!ni) {
        errno = ENOENT;
        res = -1;
        goto cleanup;
    }

    // Open the entries new parent directory
    dir_ni = ntfsOpenEntry(vd, dir);
    if (!dir_ni) {
        errno = ENOENT;
        res = -1;
        goto cleanup;
    }

    // Link the entry to its new parent
    if (ntfs_link(ni, dir_ni, uname, uname_len)) {
        res = -1;
        goto cleanup;
    }

    // Update entry times
    ntfsUpdateTimes(vd, dir_ni, NTFS_UPDATE_MCTIME);

    // Sync the entry to disc
    ntfsSync(vd, ni);

cleanup:

    if(dir_ni)
        ntfsCloseEntry(vd, dir_ni);

    if(ni)
        ntfsCloseEntry(vd, ni);

    // use free because the value was not allocated with ntfs_alloc
    if(uname)
        free(uname);

    if(dir)
        ntfs_free(dir);

    // Unlock
    ntfsUnlock(vd);

    return res;
}
Example #16
0
ntfs_inode *ntfsCreate (ntfs_vd *vd, const char *path, mode_t type, const char *target)
{
    ntfs_inode *dir_ni = NULL, *ni = NULL;
    char *dir = NULL;
    char *name = NULL;
    ntfschar *uname = NULL, *utarget = NULL;
    int uname_len, utarget_len;

    // Sanity check
    if (!vd) {
        errno = ENODEV;
        return NULL;
    }

    // You cannot link between devices
    if(target) {
        if(vd != ntfsGetVolume(target)) {
            errno = EXDEV;
            return NULL;
        }
    }

    // Get the actual paths of the entry
    path = ntfsRealPath(path);
    target = ntfsRealPath(target);
    if (!path) {
        errno = EINVAL;
        return NULL;
    }

    // Lock
    ntfsLock(vd);

    // Get the unicode name for the entry and find its parent directory
    // TODO: This looks horrible, clean it up
    dir = strdup(path);
    if (!dir) {
        errno = EINVAL;
        goto cleanup;
    }
    name = strrchr(dir, '/');
    if (name)
        name++;
    else
        name = dir;
    uname_len = ntfsLocalToUnicode(name, &uname);
    if (uname_len < 0) {
        errno = EINVAL;
        goto cleanup;
    }
    name = strrchr(dir, '/');
    if(name)
    {
        name++;
        name[0] = 0;
    }

    // Open the entries parent directory
    dir_ni = ntfsOpenEntry(vd, dir);
    if (!dir_ni) {
        goto cleanup;
    }

    // Create the entry
    switch (type) {

        // Symbolic link
        case S_IFLNK:
            if (!target) {
                errno = EINVAL;
                goto cleanup;
            }
            utarget_len = ntfsLocalToUnicode(target, &utarget);
            if (utarget_len < 0) {
                errno = EINVAL;
                goto cleanup;
            }
            ni = ntfs_create_symlink(dir_ni, 0, uname, uname_len,  utarget, utarget_len);
            break;

        // Directory or file
        case S_IFDIR:
        case S_IFREG:
            ni = ntfs_create(dir_ni, 0, uname, uname_len, type);
            break;

    }

    // If the entry was created
    if (ni) {

        // Mark the entry for archiving
        ni->flags |= FILE_ATTR_ARCHIVE;

        // Mark the entry as dirty
        NInoSetDirty(ni);

        // Sync the entry to disc
        ntfsSync(vd, ni);

        // Update parent directories times
        ntfsUpdateTimes(vd, dir_ni, NTFS_UPDATE_MCTIME);

    }

cleanup:

    if(dir_ni)
        ntfsCloseEntry(vd, dir_ni);

    // use free because the value was not allocated with ntfs_alloc
    if(utarget)
        free(utarget);

    if(uname)
        free(uname);

    if(dir)
        ntfs_free(dir);

    // Unlock
    ntfsUnlock(vd);

    return ni;
}