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 | */ |
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 | 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; |
228 | err_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 | */ |
244 | int 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; |
308 | err_out: |
309 | if (na) |
310 | ntfs_attr_close(na); |
311 | free(new_al); |
312 | errno = err; |
313 | return -1; |
314 | } |
315 |