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