1/* volume_util.c - volume functions
2 *
3 * Copyright (c) 2006-2007 Troeglazov Gerasim (3dEyes**)
4 *
5 * This program/include file is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as published
7 * by the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program/include file is distributed in the hope that it will be
11 * useful, but WITHOUT ANY WARRANTY; without even the implied warranty
12 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program (in the main directory of the Linux-NTFS
17 * distribution in the file COPYING); if not, write to the Free Software
18 * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19 */
20
21
22#include <stdlib.h>
23#include <string.h>
24#include <unistd.h>
25
26#include "types.h"
27#include "support.h"
28#include "endians.h"
29#include "bootsect.h"
30#include "device.h"
31#include "attrib.h"
32#include "volume.h"
33#include "mft.h"
34#include "bitmap.h"
35#include "inode.h"
36#include "runlist.h"
37#include "utils.h"
38#include "ntfs.h"
39#include "volume_util.h"
40
41static unsigned char ntfs_bits_table[] = { 4, 3, 3, 2, 3, 2, 2, 1,	3, 2, 2, 1, 2, 1, 1, 0 };
42
43static uint8 ntfs_count_bits(unsigned char byte, unsigned char shift)
44{
45	int i;
46	unsigned char counter = 0;
47
48	if(shift < 8)
49	{
50	 	for(i=0; i<shift; i++)
51		{
52			if(!(byte & 0x80))counter++;
53			byte = byte << 1;
54		}
55	}
56	else
57	{
58		counter += ntfs_bits_table[byte & 0x0F];
59		counter += ntfs_bits_table[(byte & 0xF0) >> 4];
60	}
61
62	return counter;
63}
64
65
66int ntfs_calc_free_space(nspace *_ns)
67{
68	nspace		*ns = (nspace*)_ns;
69	ntfs_volume *vol = ns->ntvol;
70	ntfs_attr 	*data = vol->lcnbmp_na;
71	s64 		free_clusters = 0;
72	off_t 	 	pos = 0;
73	size_t 	 	readed;
74	unsigned 	char *buf = NULL;
75
76	if(ns== NULL || vol==NULL || data==NULL)
77		return -1;
78
79	if(vol->lcnbmp_na==NULL)
80		return -1;
81
82	if (!(ns->state & NF_FreeClustersOutdate))
83		return -1;
84
85	buf = (unsigned char*)ntfs_malloc(vol->cluster_size);
86	if(!buf)goto exit;
87
88	while( pos < data->data_size)
89	{
90		if( pos % vol->cluster_size == 0)
91		{
92			readed = ntfs_attr_pread(vol->lcnbmp_na, pos, min(data->data_size - pos, vol->cluster_size), buf);
93			if(readed < B_NO_ERROR) goto error;
94			if(readed != min(data->data_size - pos, vol->cluster_size))
95				goto error;
96		}
97
98		free_clusters += ntfs_count_bits( buf[pos%vol->cluster_size], min( (vol->nr_clusters) - (pos * 8), 8));
99		pos++;
100	}
101error:
102	if(buf)
103		free(buf);
104exit:
105	ns->free_clusters = free_clusters;
106	ns->state &= ~(NF_FreeClustersOutdate);
107
108	return B_NO_ERROR;
109}
110
111
112static int resize_resident_attribute_value(MFT_RECORD *m, ATTR_RECORD *a,
113		const u32 new_vsize)
114{
115	int new_alen, new_muse;
116
117	/* New attribute length and mft record bytes used. */
118	new_alen = (le16_to_cpu(a->value_offset) + new_vsize + 7) & ~7;
119	new_muse = le32_to_cpu(m->bytes_in_use) - le32_to_cpu(a->length) +
120			new_alen;
121	/* Check for sufficient space. */
122	if ((u32)new_muse > le32_to_cpu(m->bytes_allocated)) {
123		errno = ENOSPC;
124		return -1;
125	}
126	/* Move attributes behind @a to their new location. */
127	memmove((char*)a + new_alen, (char*)a + le32_to_cpu(a->length),
128			le32_to_cpu(m->bytes_in_use) - ((char*)a - (char*)m) -
129			le32_to_cpu(a->length));
130	/* Adjust @m to reflect change in used space. */
131	m->bytes_in_use = cpu_to_le32(new_muse);
132	/* Adjust @a to reflect new value size. */
133	a->length = cpu_to_le32(new_alen);
134	a->value_length = cpu_to_le32(new_vsize);
135	return 0;
136}
137
138
139int ntfs_change_label(ntfs_volume *vol, char *label)
140{
141	ntfs_attr_search_ctx *ctx;
142	ntfschar *new_label = NULL;
143	ATTR_RECORD *a;
144	int label_len;
145	int result = 0;
146
147	ctx = ntfs_attr_get_search_ctx(vol->vol_ni, NULL);
148	if (!ctx) {
149		ntfs_log_perror("Failed to get attribute search context");
150		goto err_out;
151	}
152	if (ntfs_attr_lookup(AT_VOLUME_NAME, AT_UNNAMED, 0, 0, 0, NULL, 0,
153			ctx)) {
154		if (errno != ENOENT) {
155			ntfs_log_perror("Lookup of $VOLUME_NAME attribute failed");
156			goto err_out;
157		}
158		/* The volume name attribute does not exist.  Need to add it. */
159		a = NULL;
160	} else {
161		a = ctx->attr;
162		if (a->non_resident) {
163			ntfs_log_error("Error: Attribute $VOLUME_NAME must be "
164					"resident.\n");
165			goto err_out;
166		}
167	}
168	label_len = ntfs_mbstoucs(label, &new_label);
169	if (label_len == -1) {
170		ntfs_log_perror("Unable to convert label string to Unicode");
171		goto err_out;
172	}
173	label_len *= sizeof(ntfschar);
174	if (label_len > 0x100) {
175		ntfs_log_error("New label is too long. Maximum %u characters "
176				"allowed. Truncating excess characters.\n",
177				(unsigned)(0x100 / sizeof(ntfschar)));
178		label_len = 0x100;
179		new_label[label_len / sizeof(ntfschar)] = cpu_to_le16(L'\0');
180	}
181	if (a) {
182		if (resize_resident_attribute_value(ctx->mrec, a, label_len)) {
183			ntfs_log_perror("Error resizing resident attribute");
184			goto err_out;
185		}
186	} else {
187		/* sizeof(resident attribute record header) == 24 */
188		int asize = (24 + label_len + 7) & ~7;
189		u32 biu = le32_to_cpu(ctx->mrec->bytes_in_use);
190		if (biu + asize > le32_to_cpu(ctx->mrec->bytes_allocated)) {
191			errno = ENOSPC;
192			ntfs_log_perror("Error adding resident attribute");
193			goto err_out;
194		}
195		a = ctx->attr;
196		memmove((u8*)a + asize, a, biu - ((u8*)a - (u8*)ctx->mrec));
197		ctx->mrec->bytes_in_use = cpu_to_le32(biu + asize);
198		a->type = AT_VOLUME_NAME;
199		a->length = cpu_to_le32(asize);
200		a->non_resident = 0;
201		a->name_length = 0;
202		a->name_offset = cpu_to_le16(24);
203		a->flags = cpu_to_le16(0);
204		a->instance = ctx->mrec->next_attr_instance;
205		ctx->mrec->next_attr_instance = cpu_to_le16((le16_to_cpu(
206				ctx->mrec->next_attr_instance) + 1) & 0xffff);
207		a->value_length = cpu_to_le32(label_len);
208		a->value_offset = a->name_offset;
209		a->resident_flags = 0;
210		a->reservedR = 0;
211	}
212	memcpy((u8*)a + le16_to_cpu(a->value_offset), new_label, label_len);
213	if (ntfs_inode_sync(vol->vol_ni)) {
214		ntfs_log_perror("Error writing MFT Record to disk");
215		goto err_out;
216	}
217	result = 0;
218err_out:
219	ntfs_attr_put_search_ctx(ctx);
220	free(new_label);
221	return result;
222}
223