/*
	Copyright 1999-2001, Be Incorporated.   All Rights Reserved.
	This file may be used under the terms of the Be Sample Code License.
*/
/* attributes.c
 * handles mime type information for ntfs
 * gets/sets mime information in vnode
 */
 
 
#include "attributes.h"
 
#include <dirent.h>
#include <fs_attr.h>
#include <stdlib.h>
#include <string.h>
 
#include <KernelExport.h>
#include <SupportDefs.h>
#include <TypeConstants.h>
 
#include <kernel.h>
 
#include "mime_table.h"
#include "ntfs.h"
 
 
const char* kHaikuAttrPrefix = { "HAIKU-XATTR:" };
 
 
status_t
fs_open_attrib_dir(fs_volume *_vol, fs_vnode *_node, void **_cookie)
{
	nspace *ns = (nspace*)_vol->private_volume;
	vnode *node = (vnode*)_node->private_node;
	attrdircookie *cookie = NULL;
	ntfs_inode *ni = NULL;
	ntfs_attr_search_ctx *ctx = NULL;
 
	status_t result = B_NO_ERROR;
 
	TRACE("%s - ENTER\n", __FUNCTION__);
 
	LOCK_VOL(ns);
 
	ni = ntfs_inode_open(ns->ntvol, node->vnid);
	if (ni == NULL) {
		result = errno;
		goto exit;
	}
 
	ctx = ntfs_attr_get_search_ctx(ni, NULL);
	if (ctx == NULL) {
		result = errno;
		goto exit;
	}
 
	cookie = (attrdircookie*)ntfs_calloc(sizeof(attrdircookie));
	if (cookie == NULL) {
		result = ENOMEM;
		goto exit;
	}
 
	cookie->inode = ni;
	cookie->ctx = ctx;
	ni = NULL;
	ctx = NULL;
	*_cookie = cookie;
 
exit:
 
	if (ctx != NULL)
		ntfs_attr_put_search_ctx(ctx);
	if (ni != NULL)
		ntfs_inode_close(ni);
 
	TRACE("%s - EXIT, result is %s\n", __FUNCTION__, strerror(result));
 
	UNLOCK_VOL(ns);
 
	return result;
}
 
 
status_t
fs_close_attrib_dir(fs_volume *_vol, fs_vnode *_node, void *_cookie)
{
	return B_NO_ERROR;
}
 
 
status_t
fs_free_attrib_dir_cookie(fs_volume *_vol, fs_vnode *_node, void *_cookie)
{
	nspace *ns = (nspace *)_vol->private_volume;
	attrdircookie *cookie = (attrdircookie *)_cookie;
 
	LOCK_VOL(ns);
 
	if (cookie->ctx)
		ntfs_attr_put_search_ctx(cookie->ctx);
	if (cookie->inode)
		ntfs_inode_close(cookie->inode);
 
	UNLOCK_VOL(ns);
 
	free(cookie);
	return B_NO_ERROR;
}
 
 
status_t
fs_rewind_attrib_dir(fs_volume *_vol, fs_vnode *_node, void *_cookie)
{
	nspace *ns = (nspace*)_vol->private_volume;
	attrdircookie *cookie = (attrdircookie *)_cookie;
	status_t result = B_NO_ERROR;
 
	TRACE("%s - ENTER\n", __FUNCTION__);
 
	LOCK_VOL(ns);
 
	if (cookie->ctx)
		ntfs_attr_put_search_ctx(cookie->ctx);
	cookie->ctx = ntfs_attr_get_search_ctx(cookie->inode, NULL);
	if (cookie->ctx == NULL) {
		result = errno;
		//goto exit;
	}
 
//exit:
 
	TRACE("%s - EXIT, result is %s\n", __FUNCTION__, strerror(result));
 
	UNLOCK_VOL(ns);
 
	return result;
}
 
 
status_t
fs_read_attrib_dir(fs_volume *_vol, fs_vnode *_node, void *_cookie,
	struct dirent *entry, size_t bufsize, uint32 *num)
{
	nspace *ns = (nspace *)_vol->private_volume;
	vnode *node = (vnode *)_node->private_node;
	char *name = NULL;
	attrdircookie *cookie = (attrdircookie *)_cookie;
	uint32 numEntries = 0;
	status_t result = B_NO_ERROR;
 
	if (cookie->ctx == NULL)
		panic("cookie->ctx == NULL");
 
	LOCK_VOL(ns);
 
	TRACE("%s - ENTER\n", __FUNCTION__);
 
	while (!(result = ntfs_attrs_walk(cookie->ctx))) {
		ATTR_RECORD *attr = cookie->ctx->attr;
		if (attr->type == AT_DATA) {
			const char *real_name;
			// it's the actual file body
			if (attr->name_length == 0)
				continue;
 
			name = ntfs_attr_name_get((const ntfschar *)(((char *)attr)
				+ attr->name_offset), attr->name_length);
 
			if(strncmp(name, kHaikuAttrPrefix, strlen(kHaikuAttrPrefix)) !=0 ) {
				TRACE("found AT_DATA '%s' - Skip\n", name);
				continue;
			}
			TRACE("found AT_DATA '%s' - Found\n", name);
 
			real_name = name + strlen(kHaikuAttrPrefix);
 
			bufsize = MIN(bufsize, sizeof(struct dirent) + strlen(real_name) + 1);
			entry->d_ino = node->vnid;
			entry->d_dev = ns->id;
			entry->d_reclen = sizeof(struct dirent) + strlen(real_name);
			//XXX size
			strcpy(entry->d_name, real_name);
			ntfs_attr_name_free(&name);
			numEntries++;
			if (numEntries >= *num)
				break;
			break;
		}
	}
	if (result && errno != ENOENT) {
		result = errno;
		goto exit;
	} else
		result = B_OK;
 
exit:
 
	TRACE("%s - EXIT, result is %s, *num %d\n", __FUNCTION__,
		strerror(result), *num);
 
	UNLOCK_VOL(ns);
 
	if (result)
		*num = 0;
	else
		*num = numEntries;
 
	return result;
}
 
 
status_t
fs_create_attrib(fs_volume *_vol, fs_vnode *_node, const char* name,
	uint32 type, int openMode, void** _cookie)
{
	nspace *ns = (nspace*)_vol->private_volume;
	vnode *node = (vnode*)_node->private_node;
	attrcookie *cookie = NULL;
	ntfschar *uname = NULL;
	int ulen;
	ntfs_inode *ni = NULL;
	ntfs_attr *na = NULL;
	status_t result = B_NO_ERROR;
 
	if (ns->flags & B_FS_IS_READONLY) {
		return B_READ_ONLY_DEVICE;
	}
 
	TRACE("%s - ENTER - name: [%s] vnid: %d\n", __FUNCTION__, name, node->vnid);
 
	LOCK_VOL(ns);
 
	if (node == NULL) {
		result = EINVAL;
		goto exit;
	}
 
	ni = ntfs_inode_open(ns->ntvol, node->vnid);
	if (ni == NULL) {
		result = errno;
		TRACE("%s - inode_open: %s\n", __FUNCTION__, strerror(result));
		goto exit;
	}
 
	// UXA demangling TODO
 
	// check for EA first... TODO: WRITEME
 
	// check for a named stream
	if (true) {
		char ntfs_attr_name[MAX_PATH] = {0};
		strlcpy(ntfs_attr_name, kHaikuAttrPrefix, sizeof(ntfs_attr_name));
		strlcat(ntfs_attr_name, name, sizeof(ntfs_attr_name));
 
		uname = ntfs_calloc(MAX_PATH);
		ulen = ntfs_mbstoucs(ntfs_attr_name, &uname);
		if (ulen < 0) {
			result = EILSEQ;
			TRACE("%s - mb alloc: %s\n", __FUNCTION__, strerror(result));
			goto exit;
		}
 
		na = ntfs_attr_open(ni, AT_DATA, uname, ulen);
		if (na != NULL) {
			if (ntfs_attr_truncate(na, 0)) {
				result = errno;
				goto exit;
			}
		} else {
			if (ntfs_attr_add(ni, AT_DATA, uname, ulen, NULL, 0) < 0) {
				result = errno;
				TRACE("%s - ntfs_attr_add: %s\n", __FUNCTION__,
					strerror(result));
				goto exit;
			}
			na = ntfs_attr_open(ni, AT_DATA, uname, ulen);
			if (na == NULL) {
				result = errno;
				TRACE("%s - ntfs_attr_open: %s\n", __FUNCTION__,
					strerror(result));
				goto exit;
			}
		}
		if(ntfs_attr_pwrite(na, 0, sizeof(uint32), &type) < 0 ) {
			result = errno;
			goto exit;
		}
	}
 
	cookie = (attrcookie*)ntfs_calloc(sizeof(attrcookie));
 
	if (cookie != NULL) {
		cookie->omode = openMode;
		*_cookie = (void*)cookie;
		cookie->vnid = node->vnid;
		cookie->uname = uname;
		cookie->uname_len = ulen;
		cookie->type = type;
		uname = NULL;
	} else
		result = ENOMEM;
 
exit:
	if (uname != NULL)
		free(uname);
 
	if (na != NULL)
		ntfs_attr_close(na);
 
	if (ni != NULL)
		ntfs_inode_close(ni);
 
	TRACE("%s - EXIT, result is %s\n", __FUNCTION__, strerror(result));
 
	UNLOCK_VOL(ns);
 
	return result;
}
 
 
status_t
fs_open_attrib(fs_volume *_vol, fs_vnode *_node, const char *name,
	int openMode, void **_cookie)
{
	nspace *ns = (nspace*)_vol->private_volume;
	vnode *node = (vnode*)_node->private_node;
	attrcookie *cookie = NULL;
	ntfschar *uname = NULL;
	int ulen;
	ntfs_inode *ni = NULL;
	ntfs_attr *na = NULL;
	status_t result = B_NO_ERROR;
	uint32	type = B_XATTR_TYPE;
 
	TRACE("%s - ENTER - name: [%s] vnid: %d\n", __FUNCTION__, name,  node->vnid);
 
	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;
	}
 
	// UXA demangling TODO
 
	// check for EA first... TODO: WRITEME
 
	// check for a named stream
	if (true) {
		char ntfs_attr_name[MAX_PATH] = {0};
		strlcpy(ntfs_attr_name, kHaikuAttrPrefix, sizeof(ntfs_attr_name));
		strlcat(ntfs_attr_name, name, sizeof(ntfs_attr_name));
 
		uname = ntfs_calloc(MAX_PATH);
		ulen = ntfs_mbstoucs(ntfs_attr_name, &uname);
		if (ulen < 0) {
			result = EILSEQ;
			goto exit;
		}
 
		na = ntfs_attr_open(ni, AT_DATA, uname, ulen);
		if (na != NULL) {
			if (openMode & O_TRUNC) {
				if (ns->flags & B_FS_IS_READONLY) {
					result = B_READ_ONLY_DEVICE;
					goto exit;
				} else {
					if (ntfs_attr_truncate(na, sizeof(uint32))) {
						result = errno;
						goto exit;
					}
				}
			}
			if (ntfs_attr_pread(na, 0, sizeof(uint32), &type) != sizeof(uint32)) {
				result = errno;
				goto exit;
			}
		} else {
			result = ENOENT;
			goto exit;
		}
	}
 
 
	cookie = (attrcookie*)ntfs_calloc(sizeof(attrcookie));
 
	if (cookie != NULL) {
		cookie->omode = openMode;
		cookie->vnid = node->vnid;
		cookie->uname = uname;
		cookie->uname_len = ulen;
		cookie->type = type;
		*_cookie = (void*)cookie;
		uname = NULL;
	} else
		result = ENOMEM;
 
exit:
	if (uname != NULL)
		free(uname);
 
	if (na != NULL)
		ntfs_attr_close(na);
 
	if (ni != NULL)
		ntfs_inode_close(ni);
 
	TRACE("%s - EXIT, result is %s\n", __FUNCTION__, strerror(result));
 
	UNLOCK_VOL(ns);
 
	return result;
}
 
 
status_t
fs_close_attrib(fs_volume *_vol, fs_vnode *_node, void *cookie)
{
	TRACE("%s vnid: %d\n", __FUNCTION__, ((vnode*)_node->private_node)->vnid);
	return B_NO_ERROR;
}
 
 
status_t
fs_free_attrib_cookie(fs_volume *_vol, fs_vnode *_node, void *_cookie)
{
	nspace *ns = (nspace*)_vol->private_volume;
	attrcookie *cookie = (attrcookie *)_cookie;
 
	LOCK_VOL(ns);
 
	if (cookie->uname != NULL)
		free(cookie->uname);
 
	UNLOCK_VOL(ns);
 
	free(cookie);
	return B_NO_ERROR;
}
 
 
status_t
fs_read_attrib_stat(fs_volume *_vol, fs_vnode *_node, void *_cookie,
	struct stat *stat)
{
	nspace *ns = (nspace *)_vol->private_volume;
	vnode *node = (vnode *)_node->private_node;
	attrcookie *cookie = (attrcookie *)_cookie;
	ntfs_inode *ni = NULL;
	ntfs_attr *na = NULL;
	status_t result = B_OK;
 
	LOCK_VOL(ns);
 
	ni = ntfs_inode_open(ns->ntvol, node->vnid);
	if (ni == NULL) {
		result = errno;
		goto exit;
	}
	na = ntfs_attr_open(ni, AT_DATA, cookie->uname, cookie->uname_len);
	if (na == NULL) {
		result = errno;
		goto exit;
	}
 
	stat->st_type = cookie->type;
	stat->st_size = na ? na->data_size - sizeof(uint32) : 0;
 
exit:
	if (na != NULL)
		ntfs_attr_close(na);
	if (ni != NULL)
		ntfs_inode_close(ni);
 
	UNLOCK_VOL(ns);
 
	return result;
}
 
 
status_t
fs_read_attrib(fs_volume *_vol, fs_vnode *_node, void *_cookie, off_t pos,
	void *_buffer, size_t *len)
{
	nspace *ns = (nspace *)_vol->private_volume;
	vnode *node = (vnode *)_node->private_node;
	attrcookie *cookie = (attrcookie *)_cookie;
	ntfs_inode *ni = NULL;
	ntfs_attr *na = NULL;
	size_t size = *len;
	int total = 0;
	status_t result = B_OK;
	void *tempBuffer = NULL;
	addr_t buffer = (addr_t)_buffer;
 
	if (pos < 0) {
		*len = 0;
		return EINVAL;
	}
 
	LOCK_VOL(ns);
 
	TRACE("%s - ENTER  vnid: %d\n", __FUNCTION__, node->vnid);
 
	ni = ntfs_inode_open(ns->ntvol, node->vnid);
	if (ni == NULL) {
		result = errno;
		goto exit;
	}
	na = ntfs_attr_open(ni, AT_DATA, cookie->uname, cookie->uname_len);
	if (na == NULL) {
		result = errno;
		goto exit;
	}
 
	pos += sizeof(uint32);
 
	// it is a named stream
	if (na == NULL) {
		*len = 0;
		result = ENOENT; // TODO
		goto exit;
	}
 
	if (pos + size > na->data_size)
		size = na->data_size - pos;
 
	while (size > 0) {
		s64 bytesRead;
		s64 sizeToRead = size;
		if (tempBuffer != NULL && size > TEMP_BUFFER_SIZE)
			sizeToRead = TEMP_BUFFER_SIZE;
		bytesRead = ntfs_attr_pread(na, pos, sizeToRead,
			tempBuffer != NULL ? tempBuffer : (void*)buffer);
		if (bytesRead <= 0) {
			*len = 0;
			result = errno;
			goto exit;
		}
		if (tempBuffer != NULL
			&& user_memcpy((void*)buffer, tempBuffer, bytesRead) < B_OK) {
			result = B_BAD_ADDRESS;
			goto exit;
		}
		buffer += bytesRead;
		size -= bytesRead;
		pos += bytesRead;
		total += bytesRead;
		if (bytesRead < sizeToRead) {
			ntfs_log_error("ntfs_attr_pread returned less bytes than "
				"requested.\n");
			break;
		}
	}
 
	*len = total;
 
exit:
	free(tempBuffer);
	if (na != NULL)
		ntfs_attr_close(na);
	if (ni != NULL)
		ntfs_inode_close(ni);
 
	TRACE("%s - EXIT, result is %s\n", __FUNCTION__, strerror(result));
 
	UNLOCK_VOL(ns);
 
	return result;
}
 
 
status_t
fs_write_attrib(fs_volume *_vol, fs_vnode *_node, void *_cookie, off_t pos,
	const void *_buffer, size_t *_length)
{
	nspace *ns = (nspace *)_vol->private_volume;
	vnode *node = (vnode *)_node->private_node;
	attrcookie *cookie = (attrcookie *)_cookie;
	ntfs_inode *ni = NULL;
	ntfs_attr *na = NULL;
	size_t  size = *_length;
	char *attr_name = NULL;
	char *real_name = NULL;
	size_t total = 0;
	status_t result = B_OK;
	void *tempBuffer = NULL;
	addr_t buffer = (addr_t)_buffer;
 
	TRACE("%s - ENTER  vnode: %d\n", __FUNCTION__, node->vnid);
 
	if (ns->flags & B_FS_IS_READONLY) {
		return B_READ_ONLY_DEVICE;
	}
 
	if (pos < 0) {
		*_length = 0;
		return EINVAL;
	}
 
	LOCK_VOL(ns);
 
	ni = ntfs_inode_open(ns->ntvol, node->vnid);
	if (ni == NULL) {
		result = errno;
		goto exit;
	}
	na = ntfs_attr_open(ni, AT_DATA, cookie->uname, cookie->uname_len);
	if (na == NULL) {
		result = errno;
		goto exit;
	}
 
	pos += sizeof(uint32);
 
	// it is a named stream
	if (na == NULL) {
		*_length = 0;
		result =  EINVAL;
		goto exit;
	}
	if (cookie->omode & O_APPEND)
		pos = na->data_size;
 
	if (pos + size > na->data_size) {
		ntfs_mark_free_space_outdated(ns);
		if (ntfs_attr_truncate(na, pos + size))
			size = na->data_size - pos;
		else
			notify_stat_changed(ns->id, -1, MREF(ni->mft_no), B_STAT_SIZE);
	}
 
	if (IS_USER_ADDRESS(_buffer)) {
		tempBuffer = malloc(TEMP_BUFFER_SIZE);
		if (tempBuffer == NULL) {
			*_length = 0;
			result = B_NO_MEMORY;
			goto exit;
		}
	}
 
	while (size > 0) {
		s64 bytesWritten;
		s64 sizeToWrite = size;
		if (tempBuffer != NULL) {
			if (size > TEMP_BUFFER_SIZE)
				sizeToWrite = TEMP_BUFFER_SIZE;
			if (user_memcpy(tempBuffer, (void*)buffer, sizeToWrite) < B_OK) {
				result = B_BAD_ADDRESS;
				goto exit;
			}
		}
		bytesWritten = ntfs_attr_pwrite(na, pos, sizeToWrite,
			tempBuffer != NULL ? tempBuffer : (void*)buffer);
		if (bytesWritten <= 0) {
			ERROR("%s - ntfs_attr_pwrite()<=0\n", __FUNCTION__);
			*_length = 0;
			result = EINVAL;
			goto exit;
		}
		buffer += bytesWritten;
		size -= bytesWritten;
		pos += bytesWritten;
		total += bytesWritten;
		if (bytesWritten < sizeToWrite) {
			ERROR("%s - ntfs_attr_pwrite returned less bytes than "
				"requested.\n", __FUNCTION__);
			break;
		}
	}
 
	*_length = total;
 
	if (ntfs_ucstombs(na->name, na->name_len, &attr_name, 0) >= 0) {
		if (attr_name != NULL) {
			if(strncmp(attr_name, kHaikuAttrPrefix, strlen(kHaikuAttrPrefix)) !=0 )
				goto exit;
			real_name = attr_name + strlen(kHaikuAttrPrefix);
			notify_attribute_changed(ns->id, -1, MREF(ni->mft_no),
				real_name, B_ATTR_CHANGED);
			free(attr_name);
		}
	}
 
exit:
	free(tempBuffer);
	if (na != NULL)
		ntfs_attr_close(na);
	if (ni != NULL)
		ntfs_inode_close(ni);
 
	TRACE("%s - EXIT, result is %s\n", __FUNCTION__, strerror(result));
 
	UNLOCK_VOL(ns);
 
	return result;
}
 
 
status_t
fs_remove_attrib(fs_volume *_vol, fs_vnode *_node, const char* name)
{
	nspace *ns = (nspace *)_vol->private_volume;
	vnode *node = (vnode *)_node->private_node;
	char ntfs_attr_name[MAX_PATH]={0};
	ntfschar *uname = NULL;
	int ulen;
	ntfs_inode *ni = NULL;
	status_t result = B_NO_ERROR;
 
	TRACE("%s - ENTER - name: [%s]\n", __FUNCTION__, name);
 
	if (ns->flags & B_FS_IS_READONLY) {
		ERROR("ntfs is read-only\n");
		return B_READ_ONLY_DEVICE;
	}
 
	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;
	}
 
	strlcpy(ntfs_attr_name, kHaikuAttrPrefix, sizeof(ntfs_attr_name));
	strlcat(ntfs_attr_name, name, sizeof(ntfs_attr_name));
 
	uname = ntfs_calloc(MAX_PATH);
	ulen = ntfs_mbstoucs(ntfs_attr_name, &uname);
	if (ulen < 0) {
		result = EILSEQ;
		goto exit;
	}
 
	if (ntfs_attr_remove(ni, AT_DATA, uname, ulen)) {
		result = ENOENT;
		goto exit;
	}
 
	if (!(ni->flags & FILE_ATTR_ARCHIVE)) {
		ni->flags |= FILE_ATTR_ARCHIVE;
		NInoFileNameSetDirty(ni);
	}
	notify_attribute_changed(ns->id, -1, MREF(ni->mft_no), name,
		B_ATTR_REMOVED);
exit:
	if (uname != NULL)
		free(uname);
 
	if (ni != NULL)
		ntfs_inode_close(ni);
 
	TRACE("%s - EXIT, result is %s\n", __FUNCTION__, strerror(result));
 
	UNLOCK_VOL(ns);
 
	return result;
}
 

V575 The null pointer is passed into 'free' function. Inspect the first argument.