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