1 /** 2 * attrlist.c - Attribute list attribute handling code. Originated from the Linux-NTFS 3 * project. 4 * 5 * Copyright (c) 2004-2005 Anton Altaparmakov 6 * Copyright (c) 2004-2005 Yura Pakhuchiy 7 * Copyright (c) 2006 Szabolcs Szakacsits 8 * 9 * This program/include file is free software; you can redistribute it and/or 10 * modify it under the terms of the GNU General Public License as published 11 * by the Free Software Foundation; either version 2 of the License, or 12 * (at your option) any later version. 13 * 14 * This program/include file is distributed in the hope that it will be 15 * useful, but WITHOUT ANY WARRANTY; without even the implied warranty 16 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 * GNU General Public License for more details. 18 * 19 * You should have received a copy of the GNU General Public License 20 * along with this program (in the main directory of the NTFS-3G 21 * distribution in the file COPYING); if not, write to the Free Software 22 * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 23 */ 24 25 #ifdef HAVE_CONFIG_H 26 #include "config.h" 27 #endif 28 29 #ifdef HAVE_STRING_H 30 #include <string.h> 31 #endif 32 #ifdef HAVE_STDLIB_H 33 #include <stdlib.h> 34 #endif 35 #ifdef HAVE_ERRNO_H 36 #include <errno.h> 37 #endif 38 39 #include "types.h" 40 #include "layout.h" 41 #include "attrib.h" 42 #include "attrlist.h" 43 #include "debug.h" 44 #include "unistr.h" 45 #include "logging.h" 46 #include "misc.h" 47 48 /** 49 * ntfs_attrlist_need - check whether inode need attribute list 50 * @ni: opened ntfs inode for which perform check 51 * 52 * Check whether all are attributes belong to one MFT record, in that case 53 * attribute list is not needed. 54 * 55 * Return 1 if inode need attribute list, 0 if not, -1 on error with errno set 56 * to the error code. If function succeed errno set to 0. The following error 57 * codes are defined: 58 * EINVAL - Invalid arguments passed to function or attribute haven't got 59 * attribute list. 60 */ 61 int ntfs_attrlist_need(ntfs_inode *ni) 62 { 63 ATTR_LIST_ENTRY *ale; 64 65 if (!ni) { 66 ntfs_log_trace("Invalid arguments.\n"); 67 errno = EINVAL; 68 return -1; 69 } 70 71 ntfs_log_trace("Entering for inode 0x%llx.\n", (long long) ni->mft_no); 72 73 if (!NInoAttrList(ni)) { 74 ntfs_log_trace("Inode haven't got attribute list.\n"); 75 errno = EINVAL; 76 return -1; 77 } 78 79 if (!ni->attr_list) { 80 ntfs_log_trace("Corrupt in-memory struct.\n"); 81 errno = EINVAL; 82 return -1; 83 } 84 85 errno = 0; 86 ale = (ATTR_LIST_ENTRY *)ni->attr_list; 87 while ((u8*)ale < ni->attr_list + ni->attr_list_size) { 88 if (MREF_LE(ale->mft_reference) != ni->mft_no) 89 return 1; 90 ale = (ATTR_LIST_ENTRY *)((u8*)ale + le16_to_cpu(ale->length)); 91 } 92 return 0; 93 } 94 95 /** 96 * ntfs_attrlist_entry_add - add an attribute list attribute entry 97 * @ni: opened ntfs inode, which contains that attribute 98 * @attr: attribute record to add to attribute list 99 * 100 * Return 0 on success and -1 on error with errno set to the error code. The 101 * following error codes are defined: 102 * EINVAL - Invalid arguments passed to function. 103 * ENOMEM - Not enough memory to allocate necessary buffers. 104 * EIO - I/O error occurred or damaged filesystem. 105 * EEXIST - Such attribute already present in attribute list. 106 */ 107 int ntfs_attrlist_entry_add(ntfs_inode *ni, ATTR_RECORD *attr) 108 { 109 ATTR_LIST_ENTRY *ale; 110 MFT_REF mref; 111 ntfs_attr *na = NULL; 112 ntfs_attr_search_ctx *ctx; 113 u8 *new_al; 114 int entry_len, entry_offset, err; 115 116 ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x.\n", 117 (long long) ni->mft_no, 118 (unsigned) le32_to_cpu(attr->type)); 119 120 if (!ni || !attr) { 121 ntfs_log_trace("Invalid arguments.\n"); 122 errno = EINVAL; 123 return -1; 124 } 125 126 mref = MK_LE_MREF(ni->mft_no, le16_to_cpu(ni->mrec->sequence_number)); 127 128 if (ni->nr_extents == -1) 129 ni = ni->base_ni; 130 131 if (!NInoAttrList(ni)) { 132 ntfs_log_trace("Attribute list isn't present.\n"); 133 errno = ENOENT; 134 return -1; 135 } 136 137 /* Determine size and allocate memory for new attribute list. */ 138 entry_len = (sizeof(ATTR_LIST_ENTRY) + sizeof(ntfschar) * 139 attr->name_length + 7) & ~7; 140 new_al = ntfs_calloc(ni->attr_list_size + entry_len); 141 if (!new_al) 142 return -1; 143 144 /* Find place for the new entry. */ 145 ctx = ntfs_attr_get_search_ctx(ni, NULL); 146 if (!ctx) { 147 err = errno; 148 ntfs_log_trace("Failed to obtain attribute search context.\n"); 149 goto err_out; 150 } 151 if (!ntfs_attr_lookup(attr->type, (attr->name_length) ? (ntfschar*) 152 ((u8*)attr + le16_to_cpu(attr->name_offset)) : 153 AT_UNNAMED, attr->name_length, CASE_SENSITIVE, 154 (attr->non_resident) ? le64_to_cpu(attr->lowest_vcn) : 155 0, (attr->non_resident) ? NULL : ((u8*)attr + 156 le16_to_cpu(attr->value_offset)), (attr->non_resident) ? 157 0 : le32_to_cpu(attr->value_length), ctx)) { 158 /* Found some extent, check it to be before new extent. */ 159 if (ctx->al_entry->lowest_vcn == attr->lowest_vcn) { 160 err = EEXIST; 161 ntfs_log_trace("Such attribute already present in the " 162 "attribute list.\n"); 163 ntfs_attr_put_search_ctx(ctx); 164 goto err_out; 165 } 166 /* Add new entry after this extent. */ 167 ale = (ATTR_LIST_ENTRY*)((u8*)ctx->al_entry + 168 le16_to_cpu(ctx->al_entry->length)); 169 } else { 170 /* Check for real errors. */ 171 if (errno != ENOENT) { 172 err = errno; 173 ntfs_log_trace("Attribute lookup failed.\n"); 174 ntfs_attr_put_search_ctx(ctx); 175 goto err_out; 176 } 177 /* No previous extents found. */ 178 ale = ctx->al_entry; 179 } 180 /* Don't need it anymore, @ctx->al_entry points to @ni->attr_list. */ 181 ntfs_attr_put_search_ctx(ctx); 182 183 /* Determine new entry offset. */ 184 entry_offset = ((u8 *)ale - ni->attr_list); 185 /* Set pointer to new entry. */ 186 ale = (ATTR_LIST_ENTRY *)(new_al + entry_offset); 187 /* Zero it to fix valgrind warning. */ 188 memset(ale, 0, entry_len); 189 /* Form new entry. */ 190 ale->type = attr->type; 191 ale->length = cpu_to_le16(entry_len); 192 ale->name_length = attr->name_length; 193 ale->name_offset = offsetof(ATTR_LIST_ENTRY, name); 194 if (attr->non_resident) 195 ale->lowest_vcn = attr->lowest_vcn; 196 else 197 ale->lowest_vcn = 0; 198 ale->mft_reference = mref; 199 ale->instance = attr->instance; 200 memcpy(ale->name, (u8 *)attr + le16_to_cpu(attr->name_offset), 201 attr->name_length * sizeof(ntfschar)); 202 203 /* Resize $ATTRIBUTE_LIST to new length. */ 204 na = ntfs_attr_open(ni, AT_ATTRIBUTE_LIST, AT_UNNAMED, 0); 205 if (!na) { 206 err = errno; 207 ntfs_log_trace("Failed to open $ATTRIBUTE_LIST attribute.\n"); 208 goto err_out; 209 } 210 if (ntfs_attr_truncate(na, ni->attr_list_size + entry_len)) { 211 err = errno; 212 ntfs_log_trace("$ATTRIBUTE_LIST resize failed.\n"); 213 goto err_out; 214 } 215 216 /* Copy entries from old attribute list to new. */ 217 memcpy(new_al, ni->attr_list, entry_offset); 218 memcpy(new_al + entry_offset + entry_len, ni->attr_list + 219 entry_offset, ni->attr_list_size - entry_offset); 220 221 /* Set new runlist. */ 222 free(ni->attr_list); 223 ni->attr_list = new_al; 224 ni->attr_list_size = ni->attr_list_size + entry_len; 225 NInoAttrListSetDirty(ni); 226 /* Done! */ 227 ntfs_attr_close(na); 228 return 0; 229 err_out: 230 if (na) 231 ntfs_attr_close(na); 232 free(new_al); 233 errno = err; 234 return -1; 235 } 236 237 /** 238 * ntfs_attrlist_entry_rm - remove an attribute list attribute entry 239 * @ctx: attribute search context describing the attribute list entry 240 * 241 * Remove the attribute list entry @ctx->al_entry from the attribute list. 242 * 243 * Return 0 on success and -1 on error with errno set to the error code. 244 */ 245 int ntfs_attrlist_entry_rm(ntfs_attr_search_ctx *ctx) 246 { 247 u8 *new_al; 248 int new_al_len; 249 ntfs_inode *base_ni; 250 ntfs_attr *na; 251 ATTR_LIST_ENTRY *ale; 252 int err; 253 254 if (!ctx || !ctx->ntfs_ino || !ctx->al_entry) { 255 ntfs_log_trace("Invalid arguments.\n"); 256 errno = EINVAL; 257 return -1; 258 } 259 260 if (ctx->base_ntfs_ino) 261 base_ni = ctx->base_ntfs_ino; 262 else 263 base_ni = ctx->ntfs_ino; 264 ale = ctx->al_entry; 265 266 ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x, lowest_vcn %lld.\n", 267 (long long) ctx->ntfs_ino->mft_no, 268 (unsigned) le32_to_cpu(ctx->al_entry->type), 269 (long long) le64_to_cpu(ctx->al_entry->lowest_vcn)); 270 271 if (!NInoAttrList(base_ni)) { 272 ntfs_log_trace("Attribute list isn't present.\n"); 273 errno = ENOENT; 274 return -1; 275 } 276 277 /* Allocate memory for new attribute list. */ 278 new_al_len = base_ni->attr_list_size - le16_to_cpu(ale->length); 279 new_al = ntfs_calloc(new_al_len); 280 if (!new_al) 281 return -1; 282 283 /* Reisze $ATTRIBUTE_LIST to new length. */ 284 na = ntfs_attr_open(base_ni, AT_ATTRIBUTE_LIST, AT_UNNAMED, 0); 285 if (!na) { 286 err = errno; 287 ntfs_log_trace("Failed to open $ATTRIBUTE_LIST attribute.\n"); 288 goto err_out; 289 } 290 if (ntfs_attr_truncate(na, new_al_len)) { 291 err = errno; 292 ntfs_log_trace("$ATTRIBUTE_LIST resize failed.\n"); 293 goto err_out; 294 } 295 296 /* Copy entries from old attribute list to new. */ 297 memcpy(new_al, base_ni->attr_list, (u8*)ale - base_ni->attr_list); 298 memcpy(new_al + ((u8*)ale - base_ni->attr_list), (u8*)ale + le16_to_cpu( 299 ale->length), new_al_len - ((u8*)ale - base_ni->attr_list)); 300 301 /* Set new runlist. */ 302 free(base_ni->attr_list); 303 base_ni->attr_list = new_al; 304 base_ni->attr_list_size = new_al_len; 305 NInoAttrListSetDirty(base_ni); 306 /* Done! */ 307 ntfs_attr_close(na); 308 return 0; 309 err_out: 310 if (na) 311 ntfs_attr_close(na); 312 free(new_al); 313 errno = err; 314 return -1; 315 } 316