xref: /haiku/src/add-ons/kernel/file_systems/ntfs/libntfs/ea.c (revision 1deede7388b04dbeec5af85cae7164735ea9e70d)
1 /**
2  * ea.c - Processing of EA's
3  *
4  *      This module is part of ntfs-3g library
5  *
6  * Copyright (c) 2014 Jean-Pierre Andre
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 NTFS-3G
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_STDIO_H
29 #include <stdio.h>
30 #endif
31 #ifdef HAVE_STDLIB_H
32 #include <stdlib.h>
33 #endif
34 #ifdef HAVE_STRING_H
35 #include <string.h>
36 #endif
37 #ifdef HAVE_FCNTL_H
38 #include <fcntl.h>
39 #endif
40 #ifdef HAVE_UNISTD_H
41 #include <unistd.h>
42 #endif
43 #ifdef HAVE_ERRNO_H
44 #include <errno.h>
45 #endif
46 
47 #include "types.h"
48 #include "param.h"
49 #include "layout.h"
50 #include "attrib.h"
51 #include "index.h"
52 #include "dir.h"
53 #include "ea.h"
54 #include "misc.h"
55 #include "logging.h"
56 #include "xattrs.h"
57 
58 /*
59  *		Create a needed attribute (EA or EA_INFORMATION)
60  *
61  *	Returns 0 if successful,
62  *		-1 otherwise, with errno indicating why it failed.
63  */
64 
65 static int ntfs_need_ea(ntfs_inode *ni, ATTR_TYPES type, int size, int flags)
66 {
67 	u8 dummy;
68 	int res;
69 
70 	res = 0;
71 	if (!ntfs_attr_exist(ni,type, AT_UNNAMED,0)) {
72 		if (!(flags & XATTR_REPLACE)) {
73 			/*
74 			 * no needed attribute : add one,
75 			 * apparently, this does not feed the new value in
76 			 * Note : NTFS version must be >= 3
77 			 */
78 			if (ni->vol->major_ver >= 3) {
79 				res = ntfs_attr_add(ni,	type,
80 					AT_UNNAMED,0,&dummy,(s64)size);
81 				if (!res) {
82 					    NInoFileNameSetDirty(ni);
83 				}
84 				NInoSetDirty(ni);
85 			} else {
86 				errno = EOPNOTSUPP;
87 				res = -1;
88 			}
89 		} else {
90 			errno = ENODATA;
91 			res = -1;
92 		}
93 	}
94 	return (res);
95 }
96 
97 /*
98  *		Restore the old EA_INFORMATION or delete the current one,
99  *	 when EA cannot be updated.
100  *
101  *	As this is used in the context of some other error, the caller
102  *	is responsible for returning the proper error, and errno is
103  *	left unchanged.
104  *	Only double errors are logged here.
105  */
106 
107 static void restore_ea_info(ntfs_attr *nai, const EA_INFORMATION *old_ea_info)
108 {
109 	s64 written;
110 	int olderrno;
111 
112 	olderrno = errno;
113 	if (old_ea_info) {
114 		written = ntfs_attr_pwrite(nai,	0, sizeof(EA_INFORMATION),
115 				old_ea_info);
116 		if ((size_t)written != sizeof(EA_INFORMATION)) {
117 			ntfs_log_error("Could not restore the EA_INFORMATION,"
118 				" possible inconsistency in inode %lld\n",
119 				(long long)nai->ni->mft_no);
120 		}
121 	} else {
122 		if (ntfs_attr_rm(nai)) {
123 			ntfs_log_error("Could not delete the EA_INFORMATION,"
124 				" possible inconsistency in inode %lld\n",
125 				(long long)nai->ni->mft_no);
126 		}
127 	}
128 	errno = olderrno;
129 }
130 
131 /*
132  *		Update both EA and EA_INFORMATION
133  */
134 
135 static int ntfs_update_ea(ntfs_inode *ni, const char *value, size_t size,
136 			const EA_INFORMATION *ea_info,
137 			const EA_INFORMATION *old_ea_info)
138 {
139 	ntfs_attr *na;
140 	ntfs_attr *nai;
141 	int res;
142 
143 	res = 0;
144 	nai = ntfs_attr_open(ni, AT_EA_INFORMATION, AT_UNNAMED, 0);
145 	if (nai) {
146 		na = ntfs_attr_open(ni, AT_EA, AT_UNNAMED, 0);
147 		if (na) {
148 				/*
149 				 * Set EA_INFORMATION first, it is easier to
150 				 * restore the old value, if setting EA fails.
151 				 */
152 			if (ntfs_attr_pwrite(nai, 0, sizeof(EA_INFORMATION),
153 						ea_info)
154 					!= (s64)sizeof(EA_INFORMATION)) {
155 				res = -errno;
156 			} else {
157 				if (((na->data_size > (s64)size)
158 					&& ntfs_attr_truncate(na, size))
159 				    || (ntfs_attr_pwrite(na, 0, size, value)
160 							!= (s64)size)) {
161 					res = -errno;
162                                         if (old_ea_info)
163 						restore_ea_info(nai,
164 							old_ea_info);
165 				}
166 			}
167 			ntfs_attr_close(na);
168 		}
169 		ntfs_attr_close(nai);
170 	} else {
171 		res = -errno;
172 	}
173 	return (res);
174 }
175 
176 /*
177  *		Return the existing EA
178  *
179  *	The EA_INFORMATION is not examined and the consistency of the
180  *	existing EA is not checked.
181  *
182  *	If successful, the full attribute is returned unchanged
183  *		and its size is returned.
184  *	If the designated buffer is too small, the needed size is
185  *		returned, and the buffer is left unchanged.
186  *	If there is an error, a negative value is returned and errno
187  *		is set according to the error.
188  */
189 
190 int ntfs_get_ntfs_ea(ntfs_inode *ni, char *value, size_t size)
191 {
192 	s64 ea_size;
193 	void *ea_buf;
194 	int res = 0;
195 
196 	if (ntfs_attr_exist(ni, AT_EA, AT_UNNAMED, 0)) {
197 		ea_buf = ntfs_attr_readall(ni, AT_EA, (ntfschar*)NULL, 0,
198 					&ea_size);
199 		if (ea_buf) {
200 			if (value && (ea_size <= (s64)size))
201 				memcpy(value, ea_buf, ea_size);
202 			free(ea_buf);
203 			res = ea_size;
204 		} else {
205 			ntfs_log_error("Failed to read EA from inode %lld\n",
206 					(long long)ni->mft_no);
207 			errno = ENODATA;
208 			res = -errno;
209 		}
210 	} else {
211 		errno = ENODATA;
212 		res = -errno;
213 	}
214 	return (res);
215 }
216 
217 /*
218  *		Set a new EA, and set EA_INFORMATION accordingly
219  *
220  *	This is roughly the same as ZwSetEaFile() on Windows, however
221  *	the "offset to next" of the last EA should not be cleared.
222  *
223  *	Consistency of the new EA is first checked.
224  *
225  *	EA_INFORMATION is set first, and it is restored to its former
226  *	state if setting EA fails.
227  *
228  *	Returns 0 if successful
229  *		a negative value if an error occurred.
230  */
231 
232 int ntfs_set_ntfs_ea(ntfs_inode *ni, const char *value, size_t size, int flags)
233 {
234 	EA_INFORMATION ea_info;
235 	EA_INFORMATION *old_ea_info;
236 	s64 old_ea_size;
237 	int res;
238 	size_t offs;
239 	size_t nextoffs;
240 	BOOL ok;
241 	int ea_count;
242 	int ea_packed;
243 	const EA_ATTR *p_ea;
244 
245 	res = -1;
246 	if (value && (size > 0)) {
247 					/* do consistency checks */
248 		offs = 0;
249 		ok = TRUE;
250 		ea_count = 0;
251 		ea_packed = 0;
252 		nextoffs = 0;
253 		while (ok && (offs < size)) {
254 			p_ea = (const EA_ATTR*)&value[offs];
255 			nextoffs = offs + le32_to_cpu(p_ea->next_entry_offset);
256 				/* null offset to next not allowed */
257 			ok = (nextoffs > offs)
258 			    && (nextoffs <= size)
259 			    && !(nextoffs & 3)
260 			    && p_ea->name_length
261 				/* zero sized value are allowed */
262 			    && ((offs + offsetof(EA_ATTR,name)
263 				+ p_ea->name_length + 1
264 				+ le16_to_cpu(p_ea->value_length))
265 				    <= nextoffs)
266 			    && ((offs + offsetof(EA_ATTR,name)
267 				+ p_ea->name_length + 1
268 				+ le16_to_cpu(p_ea->value_length))
269 				    >= (nextoffs - 3))
270 			    && !p_ea->name[p_ea->name_length];
271 			/* name not checked, as chkdsk accepts any chars */
272 			if (ok) {
273 				if (p_ea->flags & NEED_EA)
274 					ea_count++;
275 				/*
276 				 * Assume ea_packed includes :
277 				 * 4 bytes for header (flags and lengths)
278 				 * + name length + 1
279 				 * + value length
280 				 */
281 				ea_packed += 5 + p_ea->name_length
282 					+ le16_to_cpu(p_ea->value_length);
283 				offs = nextoffs;
284 			}
285 		}
286 		/*
287 		 * EA and REPARSE_POINT exclude each other
288 		 * see http://msdn.microsoft.com/en-us/library/windows/desktop/aa364404(v=vs.85).aspx
289 		 * Also return EINVAL if REPARSE_POINT is present.
290 		 */
291 		if (ok
292 		    && !ntfs_attr_exist(ni, AT_REPARSE_POINT, AT_UNNAMED,0)) {
293 			ea_info.ea_length = cpu_to_le16(ea_packed);
294 			ea_info.need_ea_count = cpu_to_le16(ea_count);
295 			ea_info.ea_query_length = cpu_to_le32(nextoffs);
296 
297 			old_ea_size = 0;
298 			old_ea_info = NULL;
299 				/* Try to save the old EA_INFORMATION */
300 			if (ntfs_attr_exist(ni, AT_EA_INFORMATION,
301 							AT_UNNAMED, 0)) {
302 				old_ea_info = ntfs_attr_readall(ni,
303 					AT_EA_INFORMATION,
304 					(ntfschar*)NULL, 0, &old_ea_size);
305 			}
306 			/*
307 			 * no EA or EA_INFORMATION : add them
308 			 */
309 			if (!ntfs_need_ea(ni, AT_EA_INFORMATION,
310 					sizeof(EA_INFORMATION), flags)
311 			    && !ntfs_need_ea(ni, AT_EA, 0, flags)) {
312 				res = ntfs_update_ea(ni, value, size,
313 						&ea_info, old_ea_info);
314 			} else {
315 				res = -errno;
316 			}
317 			if (old_ea_info)
318 				free(old_ea_info);
319 		} else {
320 			errno = EINVAL;
321 			res = -errno;
322 		}
323 	} else {
324 		errno = EINVAL;
325 		res = -errno;
326 	}
327 	return (res);
328 }
329 
330 /*
331  *		Remove the EA (including EA_INFORMATION)
332  *
333  *	EA_INFORMATION is removed first, and it is restored to its former
334  *	state if removing EA fails.
335  *
336  *	Returns 0, or -1 if there is a problem
337  */
338 
339 int ntfs_remove_ntfs_ea(ntfs_inode *ni)
340 {
341 	EA_INFORMATION *old_ea_info;
342 	s64 old_ea_size;
343 	int res;
344 	ntfs_attr *na;
345 	ntfs_attr *nai;
346 
347 	res = 0;
348 	if (ni) {
349 		/*
350 		 * open and delete the EA_INFORMATION and the EA
351 		 */
352 		nai = ntfs_attr_open(ni, AT_EA_INFORMATION, AT_UNNAMED, 0);
353 		if (nai) {
354 			na = ntfs_attr_open(ni, AT_EA, AT_UNNAMED, 0);
355 			if (na) {
356 				/* Try to save the old EA_INFORMATION */
357 				old_ea_info = ntfs_attr_readall(ni,
358 					 AT_EA_INFORMATION,
359 					 (ntfschar*)NULL, 0, &old_ea_size);
360 				res = ntfs_attr_rm(na);
361 				NInoFileNameSetDirty(ni);
362 				if (!res) {
363 					res = ntfs_attr_rm(nai);
364 					if (res && old_ea_info) {
365 					/*
366 					 * Failed to remove the EA, try to
367 					 * restore the EA_INFORMATION
368 					 */
369 						restore_ea_info(nai,
370 							old_ea_info);
371 					}
372 				} else {
373 					ntfs_log_error("Failed to remove the"
374 						" EA_INFORMATION from inode %lld\n",
375 						(long long)ni->mft_no);
376 				}
377 				free(old_ea_info);
378 				ntfs_attr_close(na);
379 			} else {
380 				/* EA_INFORMATION present, but no EA */
381 				res = ntfs_attr_rm(nai);
382 				NInoFileNameSetDirty(ni);
383 			}
384 			ntfs_attr_close(nai);
385 		} else {
386 			errno = ENODATA;
387 			res = -1;
388 		}
389 		NInoSetDirty(ni);
390 	} else {
391 		errno = EINVAL;
392 		res = -1;
393 	}
394 	return (res ? -1 : 0);
395 }
396