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 }