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