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