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