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 }