/* volume_util.c - volume functions
 *
 * 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 <stdlib.h>
#include <string.h>
#include <unistd.h>
 
#include "types.h"
#include "support.h"
#include "endians.h"
#include "bootsect.h"
#include "device.h"
#include "attrib.h"
#include "volume.h"
#include "mft.h"
#include "bitmap.h"
#include "inode.h"
#include "runlist.h"
#include "utils.h"
#include "ntfs.h"
#include "volume_util.h"
 
static unsigned char ntfs_bits_table[] = { 4, 3, 3, 2, 3, 2, 2, 1,
	3, 2, 2, 1, 2, 1, 1, 0 };
 
static uint8 ntfs_count_bits(unsigned char byte, unsigned char shift)
{
	int i;
	unsigned char counter = 0;
	
	if (shift < 8) {
	 	for (i=0; i<shift; i++) {
			if (!(byte & 0x80))
				counter++;
			byte = byte << 1;
		}
	} else {
		counter += ntfs_bits_table[byte & 0x0F];
		counter += ntfs_bits_table[(byte & 0xF0) >> 4];			
	}
	
	return counter;
}
 
 
int ntfs_calc_free_space(nspace *_ns)
{
	nspace		*ns = (nspace*)_ns;
	ntfs_volume *vol = ns->ntvol;	
	ntfs_attr 	*data = vol->lcnbmp_na;
	s64 		free_clusters = 0;
	off_t 	 	pos = 0;
	size_t 	 	readed;
	unsigned 	char *buf = NULL;
			
	if (ns == NULL || vol == NULL || data == NULL)
		return -1;
		
	if (vol->lcnbmp_na == NULL)
		return -1;
 
	if (!(ns->state & NF_FreeClustersOutdate))
		return -1;	
 
	buf = (unsigned char*)ntfs_malloc(vol->cluster_size);
	if (buf == NULL)
		goto exit;
	
	while (pos < data->data_size) {
		if (pos % vol->cluster_size == 0) {
			readed = ntfs_attr_pread(vol->lcnbmp_na, pos,
				min(data->data_size - pos, vol->cluster_size), buf);
			if (readed < B_NO_ERROR)
				goto error;
			if (readed != min(data->data_size - pos, vol->cluster_size))
				goto error;
		}
 
		free_clusters += ntfs_count_bits( buf[pos%vol->cluster_size],
			min((vol->nr_clusters) - (pos * 8), 8));
		pos++;
	}
error:
	free(buf);
exit:	
	ns->free_clusters = free_clusters;
	ns->state &= ~(NF_FreeClustersOutdate);
	
	return B_NO_ERROR;
}
 
 
static int resize_resident_attribute_value(MFT_RECORD *m, ATTR_RECORD *a,
		const u32 new_vsize)
{
	int new_alen, new_muse;
 
	/* New attribute length and mft record bytes used. */
	new_alen = (le16_to_cpu(a->value_offset) + new_vsize + 7) & ~7;
	new_muse = le32_to_cpu(m->bytes_in_use) - le32_to_cpu(a->length) +
			new_alen;
	/* Check for sufficient space. */
	if ((u32)new_muse > le32_to_cpu(m->bytes_allocated)) {
		errno = ENOSPC;
		return -1;
	}
	/* Move attributes behind @a to their new location. */
	memmove((char*)a + new_alen, (char*)a + le32_to_cpu(a->length),
			le32_to_cpu(m->bytes_in_use) - ((char*)a - (char*)m) -
			le32_to_cpu(a->length));
	/* Adjust @m to reflect change in used space. */
	m->bytes_in_use = cpu_to_le32(new_muse);
	/* Adjust @a to reflect new value size. */
	a->length = cpu_to_le32(new_alen);
	a->value_length = cpu_to_le32(new_vsize);
	return 0;
}
 
 
int ntfs_change_label(ntfs_volume *vol, char *label)
{
	ntfs_attr_search_ctx *ctx;
	ntfschar *new_label = NULL;
	ATTR_RECORD *a;
	int label_len;
	int result = 0;
 
	ctx = ntfs_attr_get_search_ctx(vol->vol_ni, NULL);
	if (!ctx) {
		ntfs_log_perror("Failed to get attribute search context");
		goto err_out;
	}
	if (ntfs_attr_lookup(AT_VOLUME_NAME, AT_UNNAMED, 0, 0, 0, NULL, 0,
			ctx)) {
		if (errno != ENOENT) {
			ntfs_log_perror("Lookup of $VOLUME_NAME attribute failed");
			goto err_out;
		}
		/* The volume name attribute does not exist.  Need to add it. */
		a = NULL;
	} else {
		a = ctx->attr;
		if (a->non_resident) {
			ntfs_log_error("Error: Attribute $VOLUME_NAME must be "
					"resident.\n");
			goto err_out;
		}
	}
	label_len = ntfs_mbstoucs(label, &new_label);
	if (label_len == -1) {
		ntfs_log_perror("Unable to convert label string to Unicode");
		goto err_out;
	}
	label_len *= sizeof(ntfschar);
	if (label_len > 0x100) {
		ntfs_log_error("New label is too long. Maximum %u characters "
				"allowed. Truncating excess characters.\n",
				(unsigned)(0x100 / sizeof(ntfschar)));
		label_len = 0x100;
		new_label[label_len / sizeof(ntfschar)] = cpu_to_le16(L'\0');
	}
	if (a) {
		if (resize_resident_attribute_value(ctx->mrec, a, label_len)) {
			ntfs_log_perror("Error resizing resident attribute");
			goto err_out;
		}
	} else {
		/* sizeof(resident attribute record header) == 24 */
		int asize = (24 + label_len + 7) & ~7;
		u32 biu = le32_to_cpu(ctx->mrec->bytes_in_use);
		if (biu + asize > le32_to_cpu(ctx->mrec->bytes_allocated)) {
			errno = ENOSPC;
			ntfs_log_perror("Error adding resident attribute");
			goto err_out;
		}
		a = ctx->attr;
		memmove((u8*)a + asize, a, biu - ((u8*)a - (u8*)ctx->mrec));
		ctx->mrec->bytes_in_use = cpu_to_le32(biu + asize);
		a->type = AT_VOLUME_NAME;
		a->length = cpu_to_le32(asize);
		a->non_resident = 0;
		a->name_length = 0;
		a->name_offset = cpu_to_le16(24);
		a->flags = cpu_to_le16(0);
		a->instance = ctx->mrec->next_attr_instance;
		ctx->mrec->next_attr_instance = cpu_to_le16((le16_to_cpu(
				ctx->mrec->next_attr_instance) + 1) & 0xffff);
		a->value_length = cpu_to_le32(label_len);
		a->value_offset = a->name_offset;
		a->resident_flags = 0;
		a->reservedR = 0;
	}
	memcpy((u8*)a + le16_to_cpu(a->value_offset), new_label, label_len);
	if (ntfs_inode_sync(vol->vol_ni)) {
		ntfs_log_perror("Error writing MFT Record to disk");
		goto err_out;
	}
	result = 0;
err_out:
	ntfs_attr_put_search_ctx(ctx);
	free(new_label);
	return result;
}

V595 The 'vol' pointer was utilized before it was verified against nullptr. Check lines: 68, 74.

V595 The 'ns' pointer was utilized before it was verified against nullptr. Check lines: 67, 74.

V547 Expression 'readed < ((int) 0)' is always false. Unsigned type value is never < 0.