summaryrefslogtreecommitdiff
path: root/libntfs-3g/attrlist.c (plain)
blob: 9c62f316146fb4e50b82879a0d5ca110e4dc0e03
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 */
61int 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 */
107int 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 goto err_out;
149 }
150 if (!ntfs_attr_lookup(attr->type, (attr->name_length) ? (ntfschar*)
151 ((u8*)attr + le16_to_cpu(attr->name_offset)) :
152 AT_UNNAMED, attr->name_length, CASE_SENSITIVE,
153 (attr->non_resident) ? le64_to_cpu(attr->lowest_vcn) :
154 0, (attr->non_resident) ? NULL : ((u8*)attr +
155 le16_to_cpu(attr->value_offset)), (attr->non_resident) ?
156 0 : le32_to_cpu(attr->value_length), ctx)) {
157 /* Found some extent, check it to be before new extent. */
158 if (ctx->al_entry->lowest_vcn == attr->lowest_vcn) {
159 err = EEXIST;
160 ntfs_log_trace("Such attribute already present in the "
161 "attribute list.\n");
162 ntfs_attr_put_search_ctx(ctx);
163 goto err_out;
164 }
165 /* Add new entry after this extent. */
166 ale = (ATTR_LIST_ENTRY*)((u8*)ctx->al_entry +
167 le16_to_cpu(ctx->al_entry->length));
168 } else {
169 /* Check for real errors. */
170 if (errno != ENOENT) {
171 err = errno;
172 ntfs_log_trace("Attribute lookup failed.\n");
173 ntfs_attr_put_search_ctx(ctx);
174 goto err_out;
175 }
176 /* No previous extents found. */
177 ale = ctx->al_entry;
178 }
179 /* Don't need it anymore, @ctx->al_entry points to @ni->attr_list. */
180 ntfs_attr_put_search_ctx(ctx);
181
182 /* Determine new entry offset. */
183 entry_offset = ((u8 *)ale - ni->attr_list);
184 /* Set pointer to new entry. */
185 ale = (ATTR_LIST_ENTRY *)(new_al + entry_offset);
186 /* Zero it to fix valgrind warning. */
187 memset(ale, 0, entry_len);
188 /* Form new entry. */
189 ale->type = attr->type;
190 ale->length = cpu_to_le16(entry_len);
191 ale->name_length = attr->name_length;
192 ale->name_offset = offsetof(ATTR_LIST_ENTRY, name);
193 if (attr->non_resident)
194 ale->lowest_vcn = attr->lowest_vcn;
195 else
196 ale->lowest_vcn = 0;
197 ale->mft_reference = mref;
198 ale->instance = attr->instance;
199 memcpy(ale->name, (u8 *)attr + le16_to_cpu(attr->name_offset),
200 attr->name_length * sizeof(ntfschar));
201
202 /* Resize $ATTRIBUTE_LIST to new length. */
203 na = ntfs_attr_open(ni, AT_ATTRIBUTE_LIST, AT_UNNAMED, 0);
204 if (!na) {
205 err = errno;
206 ntfs_log_trace("Failed to open $ATTRIBUTE_LIST attribute.\n");
207 goto err_out;
208 }
209 if (ntfs_attr_truncate(na, ni->attr_list_size + entry_len)) {
210 err = errno;
211 ntfs_log_trace("$ATTRIBUTE_LIST resize failed.\n");
212 goto err_out;
213 }
214
215 /* Copy entries from old attribute list to new. */
216 memcpy(new_al, ni->attr_list, entry_offset);
217 memcpy(new_al + entry_offset + entry_len, ni->attr_list +
218 entry_offset, ni->attr_list_size - entry_offset);
219
220 /* Set new runlist. */
221 free(ni->attr_list);
222 ni->attr_list = new_al;
223 ni->attr_list_size = ni->attr_list_size + entry_len;
224 NInoAttrListSetDirty(ni);
225 /* Done! */
226 ntfs_attr_close(na);
227 return 0;
228err_out:
229 if (na)
230 ntfs_attr_close(na);
231 free(new_al);
232 errno = err;
233 return -1;
234}
235
236/**
237 * ntfs_attrlist_entry_rm - remove an attribute list attribute entry
238 * @ctx: attribute search context describing the attribute list entry
239 *
240 * Remove the attribute list entry @ctx->al_entry from the attribute list.
241 *
242 * Return 0 on success and -1 on error with errno set to the error code.
243 */
244int ntfs_attrlist_entry_rm(ntfs_attr_search_ctx *ctx)
245{
246 u8 *new_al;
247 int new_al_len;
248 ntfs_inode *base_ni;
249 ntfs_attr *na;
250 ATTR_LIST_ENTRY *ale;
251 int err;
252
253 if (!ctx || !ctx->ntfs_ino || !ctx->al_entry) {
254 ntfs_log_trace("Invalid arguments.\n");
255 errno = EINVAL;
256 return -1;
257 }
258
259 if (ctx->base_ntfs_ino)
260 base_ni = ctx->base_ntfs_ino;
261 else
262 base_ni = ctx->ntfs_ino;
263 ale = ctx->al_entry;
264
265 ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x, lowest_vcn %lld.\n",
266 (long long) ctx->ntfs_ino->mft_no,
267 (unsigned) le32_to_cpu(ctx->al_entry->type),
268 (long long) le64_to_cpu(ctx->al_entry->lowest_vcn));
269
270 if (!NInoAttrList(base_ni)) {
271 ntfs_log_trace("Attribute list isn't present.\n");
272 errno = ENOENT;
273 return -1;
274 }
275
276 /* Allocate memory for new attribute list. */
277 new_al_len = base_ni->attr_list_size - le16_to_cpu(ale->length);
278 new_al = ntfs_calloc(new_al_len);
279 if (!new_al)
280 return -1;
281
282 /* Reisze $ATTRIBUTE_LIST to new length. */
283 na = ntfs_attr_open(base_ni, AT_ATTRIBUTE_LIST, AT_UNNAMED, 0);
284 if (!na) {
285 err = errno;
286 ntfs_log_trace("Failed to open $ATTRIBUTE_LIST attribute.\n");
287 goto err_out;
288 }
289 if (ntfs_attr_truncate(na, new_al_len)) {
290 err = errno;
291 ntfs_log_trace("$ATTRIBUTE_LIST resize failed.\n");
292 goto err_out;
293 }
294
295 /* Copy entries from old attribute list to new. */
296 memcpy(new_al, base_ni->attr_list, (u8*)ale - base_ni->attr_list);
297 memcpy(new_al + ((u8*)ale - base_ni->attr_list), (u8*)ale + le16_to_cpu(
298 ale->length), new_al_len - ((u8*)ale - base_ni->attr_list));
299
300 /* Set new runlist. */
301 free(base_ni->attr_list);
302 base_ni->attr_list = new_al;
303 base_ni->attr_list_size = new_al_len;
304 NInoAttrListSetDirty(base_ni);
305 /* Done! */
306 ntfs_attr_close(na);
307 return 0;
308err_out:
309 if (na)
310 ntfs_attr_close(na);
311 free(new_al);
312 errno = err;
313 return -1;
314}
315