1 /** 2 * ea.c - Processing of EA's 3 * 4 * This module is part of ntfs-3g library 5 * 6 * Copyright (c) 2014-2021 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 #ifdef MAJOR_IN_MKDEV 47 #include <sys/mkdev.h> 48 #endif 49 #ifdef MAJOR_IN_SYSMACROS 50 #include <sys/sysmacros.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 #include "xattrs.h" 63 64 static const char lxdev[] = "$LXDEV"; 65 static const char lxmod[] = "$LXMOD"; 66 67 68 /* 69 * Create a needed attribute (EA or EA_INFORMATION) 70 * 71 * Returns 0 if successful, 72 * -1 otherwise, with errno indicating why it failed. 73 */ 74 75 static int ntfs_need_ea(ntfs_inode *ni, ATTR_TYPES type, int size, int flags) 76 { 77 u8 dummy; 78 int res; 79 80 res = 0; 81 if (!ntfs_attr_exist(ni,type, AT_UNNAMED,0)) { 82 if (!(flags & XATTR_REPLACE)) { 83 /* 84 * no needed attribute : add one, 85 * apparently, this does not feed the new value in 86 * Note : NTFS version must be >= 3 87 */ 88 if (ni->vol->major_ver >= 3) { 89 res = ntfs_attr_add(ni, type, 90 AT_UNNAMED,0,&dummy,(s64)size); 91 if (!res) { 92 NInoFileNameSetDirty(ni); 93 } 94 NInoSetDirty(ni); 95 } else { 96 errno = EOPNOTSUPP; 97 res = -1; 98 } 99 } else { 100 errno = ENODATA; 101 res = -1; 102 } 103 } 104 return (res); 105 } 106 107 /* 108 * Restore the old EA_INFORMATION or delete the current one, 109 * when EA cannot be updated. 110 * 111 * As this is used in the context of some other error, the caller 112 * is responsible for returning the proper error, and errno is 113 * left unchanged. 114 * Only double errors are logged here. 115 */ 116 117 static void restore_ea_info(ntfs_attr *nai, const EA_INFORMATION *old_ea_info) 118 { 119 s64 written; 120 int olderrno; 121 122 olderrno = errno; 123 if (old_ea_info) { 124 written = ntfs_attr_pwrite(nai, 0, sizeof(EA_INFORMATION), 125 old_ea_info); 126 if ((size_t)written != sizeof(EA_INFORMATION)) { 127 ntfs_log_error("Could not restore the EA_INFORMATION," 128 " possible inconsistency in inode %lld\n", 129 (long long)nai->ni->mft_no); 130 } 131 } else { 132 if (ntfs_attr_rm(nai)) { 133 ntfs_log_error("Could not delete the EA_INFORMATION," 134 " possible inconsistency in inode %lld\n", 135 (long long)nai->ni->mft_no); 136 } 137 } 138 errno = olderrno; 139 } 140 141 /* 142 * Update both EA and EA_INFORMATION 143 */ 144 145 static int ntfs_update_ea(ntfs_inode *ni, const char *value, size_t size, 146 const EA_INFORMATION *ea_info, 147 const EA_INFORMATION *old_ea_info) 148 { 149 ntfs_attr *na; 150 ntfs_attr *nai; 151 int res; 152 153 res = 0; 154 nai = ntfs_attr_open(ni, AT_EA_INFORMATION, AT_UNNAMED, 0); 155 if (nai) { 156 na = ntfs_attr_open(ni, AT_EA, AT_UNNAMED, 0); 157 if (na) { 158 /* 159 * Set EA_INFORMATION first, it is easier to 160 * restore the old value, if setting EA fails. 161 */ 162 if (ntfs_attr_pwrite(nai, 0, sizeof(EA_INFORMATION), 163 ea_info) 164 != (s64)sizeof(EA_INFORMATION)) { 165 res = -errno; 166 } else { 167 if (((na->data_size > (s64)size) 168 && ntfs_attr_truncate(na, size)) 169 || (ntfs_attr_pwrite(na, 0, size, value) 170 != (s64)size)) { 171 res = -errno; 172 if (old_ea_info) 173 restore_ea_info(nai, 174 old_ea_info); 175 } 176 } 177 ntfs_attr_close(na); 178 } 179 ntfs_attr_close(nai); 180 } else { 181 res = -errno; 182 } 183 return (res); 184 } 185 186 /* 187 * Return the existing EA 188 * 189 * The EA_INFORMATION is not examined and the consistency of the 190 * existing EA is not checked. 191 * 192 * If successful, the full attribute is returned unchanged 193 * and its size is returned. 194 * If the designated buffer is too small, the needed size is 195 * returned, and the buffer is left unchanged. 196 * If there is an error, a negative value is returned and errno 197 * is set according to the error. 198 */ 199 200 int ntfs_get_ntfs_ea(ntfs_inode *ni, char *value, size_t size) 201 { 202 s64 ea_size; 203 void *ea_buf; 204 int res = 0; 205 206 if (ntfs_attr_exist(ni, AT_EA, AT_UNNAMED, 0)) { 207 ea_buf = ntfs_attr_readall(ni, AT_EA, (ntfschar*)NULL, 0, 208 &ea_size); 209 if (ea_buf) { 210 if (value && (ea_size <= (s64)size)) 211 memcpy(value, ea_buf, ea_size); 212 free(ea_buf); 213 res = ea_size; 214 } else { 215 ntfs_log_error("Failed to read EA from inode %lld\n", 216 (long long)ni->mft_no); 217 errno = ENODATA; 218 res = -errno; 219 } 220 } else { 221 errno = ENODATA; 222 res = -errno; 223 } 224 return (res); 225 } 226 227 /* 228 * Set a new EA, and set EA_INFORMATION accordingly 229 * 230 * This is roughly the same as ZwSetEaFile() on Windows, however 231 * the "offset to next" of the last EA should not be cleared. 232 * 233 * Consistency of the new EA is first checked. 234 * 235 * EA_INFORMATION is set first, and it is restored to its former 236 * state if setting EA fails. 237 * 238 * Returns 0 if successful 239 * a negative value if an error occurred. 240 */ 241 242 int ntfs_set_ntfs_ea(ntfs_inode *ni, const char *value, size_t size, int flags) 243 { 244 EA_INFORMATION ea_info; 245 EA_INFORMATION *old_ea_info; 246 s64 old_ea_size; 247 int res; 248 size_t offs; 249 size_t nextoffs; 250 BOOL ok; 251 int ea_count; 252 int ea_packed; 253 const EA_ATTR *p_ea; 254 255 res = -1; 256 if (value && (size > 0)) { 257 /* do consistency checks */ 258 offs = 0; 259 ok = TRUE; 260 ea_count = 0; 261 ea_packed = 0; 262 nextoffs = 0; 263 while (ok && (offs < size)) { 264 p_ea = (const EA_ATTR*)&value[offs]; 265 nextoffs = offs + le32_to_cpu(p_ea->next_entry_offset); 266 /* null offset to next not allowed */ 267 ok = (nextoffs > offs) 268 && (nextoffs <= size) 269 && !(nextoffs & 3) 270 && p_ea->name_length 271 /* zero sized value are allowed */ 272 && ((offs + offsetof(EA_ATTR,name) 273 + p_ea->name_length + 1 274 + le16_to_cpu(p_ea->value_length)) 275 <= nextoffs) 276 && ((offs + offsetof(EA_ATTR,name) 277 + p_ea->name_length + 1 278 + le16_to_cpu(p_ea->value_length)) 279 >= (nextoffs - 3)) 280 && !p_ea->name[p_ea->name_length]; 281 /* name not checked, as chkdsk accepts any chars */ 282 if (ok) { 283 if (p_ea->flags & NEED_EA) 284 ea_count++; 285 /* 286 * Assume ea_packed includes : 287 * 4 bytes for header (flags and lengths) 288 * + name length + 1 289 * + value length 290 */ 291 ea_packed += 5 + p_ea->name_length 292 + le16_to_cpu(p_ea->value_length); 293 offs = nextoffs; 294 } 295 } 296 /* 297 * EA and REPARSE_POINT compatibility not checked any more, 298 * required by Windows 10, but having both may lead to 299 * problems with earlier versions. 300 */ 301 if (ok) { 302 ea_info.ea_length = cpu_to_le16(ea_packed); 303 ea_info.need_ea_count = cpu_to_le16(ea_count); 304 ea_info.ea_query_length = cpu_to_le32(nextoffs); 305 306 old_ea_size = 0; 307 old_ea_info = NULL; 308 /* Try to save the old EA_INFORMATION */ 309 if (ntfs_attr_exist(ni, AT_EA_INFORMATION, 310 AT_UNNAMED, 0)) { 311 old_ea_info = ntfs_attr_readall(ni, 312 AT_EA_INFORMATION, 313 (ntfschar*)NULL, 0, &old_ea_size); 314 } 315 /* 316 * no EA or EA_INFORMATION : add them 317 */ 318 if (!ntfs_need_ea(ni, AT_EA_INFORMATION, 319 sizeof(EA_INFORMATION), flags) 320 && !ntfs_need_ea(ni, AT_EA, 0, flags)) { 321 res = ntfs_update_ea(ni, value, size, 322 &ea_info, old_ea_info); 323 } else { 324 res = -errno; 325 } 326 if (old_ea_info) 327 free(old_ea_info); 328 } else { 329 errno = EINVAL; 330 res = -errno; 331 } 332 } else { 333 errno = EINVAL; 334 res = -errno; 335 } 336 return (res); 337 } 338 339 /* 340 * Remove the EA (including EA_INFORMATION) 341 * 342 * EA_INFORMATION is removed first, and it is restored to its former 343 * state if removing EA fails. 344 * 345 * Returns 0, or -1 if there is a problem 346 */ 347 348 int ntfs_remove_ntfs_ea(ntfs_inode *ni) 349 { 350 EA_INFORMATION *old_ea_info; 351 s64 old_ea_size; 352 int res; 353 ntfs_attr *na; 354 ntfs_attr *nai; 355 356 res = 0; 357 if (ni) { 358 /* 359 * open and delete the EA_INFORMATION and the EA 360 */ 361 nai = ntfs_attr_open(ni, AT_EA_INFORMATION, AT_UNNAMED, 0); 362 if (nai) { 363 na = ntfs_attr_open(ni, AT_EA, AT_UNNAMED, 0); 364 if (na) { 365 /* Try to save the old EA_INFORMATION */ 366 old_ea_info = ntfs_attr_readall(ni, 367 AT_EA_INFORMATION, 368 (ntfschar*)NULL, 0, &old_ea_size); 369 res = ntfs_attr_rm(na); 370 NInoFileNameSetDirty(ni); 371 if (!res) { 372 res = ntfs_attr_rm(nai); 373 if (res && old_ea_info) { 374 /* 375 * Failed to remove the EA, try to 376 * restore the EA_INFORMATION 377 */ 378 restore_ea_info(nai, 379 old_ea_info); 380 } 381 } else { 382 ntfs_log_error("Failed to remove the" 383 " EA_INFORMATION from inode %lld\n", 384 (long long)ni->mft_no); 385 } 386 free(old_ea_info); 387 ntfs_attr_close(na); 388 } else { 389 /* EA_INFORMATION present, but no EA */ 390 res = ntfs_attr_rm(nai); 391 NInoFileNameSetDirty(ni); 392 } 393 ntfs_attr_close(nai); 394 } else { 395 errno = ENODATA; 396 res = -1; 397 } 398 NInoSetDirty(ni); 399 } else { 400 errno = EINVAL; 401 res = -1; 402 } 403 return (res ? -1 : 0); 404 } 405 406 /* 407 * Check for the presence of an EA "$LXDEV" (used by WSL) 408 * and return its value as a device address 409 * 410 * Returns zero if successful 411 * -1 if failed, with errno set 412 */ 413 414 int ntfs_ea_check_wsldev(ntfs_inode *ni, dev_t *rdevp) 415 { 416 const EA_ATTR *p_ea; 417 int bufsize; 418 char *buf; 419 int lth; 420 int res; 421 int offset; 422 int next; 423 BOOL found; 424 struct { 425 le32 major; 426 le32 minor; 427 } device; 428 429 res = -EOPNOTSUPP; 430 bufsize = 256; /* expected to be enough */ 431 buf = (char*)malloc(bufsize); 432 if (buf) { 433 lth = ntfs_get_ntfs_ea(ni, buf, bufsize); 434 /* retry if short buf */ 435 if (lth > bufsize) { 436 free(buf); 437 bufsize = lth; 438 buf = (char*)malloc(bufsize); 439 if (buf) 440 lth = ntfs_get_ntfs_ea(ni, buf, bufsize); 441 } 442 } 443 if (buf && (lth > 0) && (lth <= bufsize)) { 444 offset = 0; 445 found = FALSE; 446 do { 447 p_ea = (const EA_ATTR*)&buf[offset]; 448 next = le32_to_cpu(p_ea->next_entry_offset); 449 found = ((next > (int)(sizeof(lxdev) + sizeof(device))) 450 && (p_ea->name_length == (sizeof(lxdev) - 1)) 451 && (p_ea->value_length 452 == const_cpu_to_le16(sizeof(device))) 453 && !memcmp(p_ea->name, lxdev, sizeof(lxdev))); 454 if (!found) 455 offset += next; 456 } while (!found && (next > 0) && (offset < lth)); 457 if (found) { 458 /* beware of alignment */ 459 memcpy(&device, &p_ea->name[p_ea->name_length + 1], 460 sizeof(device)); 461 *rdevp = makedev(le32_to_cpu(device.major), 462 le32_to_cpu(device.minor)); 463 res = 0; 464 } 465 } 466 free(buf); 467 return (res); 468 } 469 470 int ntfs_ea_set_wsl_not_symlink(ntfs_inode *ni, mode_t type, dev_t dev) 471 { 472 le32 mode; 473 struct { 474 le32 major; 475 le32 minor; 476 } device; 477 struct EA_WSL { 478 struct EA_LXMOD { /* always inserted */ 479 EA_ATTR base; 480 char name[sizeof(lxmod)]; 481 char value[sizeof(mode)]; 482 char stuff[3 & -(sizeof(lxmod) + sizeof(mode))]; 483 } mod; 484 struct EA_LXDEV { /* char or block devices only */ 485 EA_ATTR base; 486 char name[sizeof(lxdev)]; 487 char value[sizeof(device)]; 488 char stuff[3 & -(sizeof(lxdev) + sizeof(device))]; 489 } dev; 490 } attr; 491 int len; 492 int res; 493 494 memset(&attr, 0, sizeof(attr)); 495 mode = cpu_to_le32((u32)(type | 0644)); 496 attr.mod.base.next_entry_offset 497 = const_cpu_to_le32(sizeof(attr.mod)); 498 attr.mod.base.flags = 0; 499 attr.mod.base.name_length = sizeof(lxmod) - 1; 500 attr.mod.base.value_length = const_cpu_to_le16(sizeof(mode)); 501 memcpy(attr.mod.name, lxmod, sizeof(lxmod)); 502 memcpy(attr.mod.value, &mode, sizeof(mode)); 503 len = sizeof(attr.mod); 504 505 if (S_ISCHR(type) || S_ISBLK(type)) { 506 device.major = cpu_to_le32(major(dev)); 507 device.minor = cpu_to_le32(minor(dev)); 508 attr.dev.base.next_entry_offset 509 = const_cpu_to_le32(sizeof(attr.dev)); 510 attr.dev.base.flags = 0; 511 attr.dev.base.name_length = sizeof(lxdev) - 1; 512 attr.dev.base.value_length = const_cpu_to_le16(sizeof(device)); 513 memcpy(attr.dev.name, lxdev, sizeof(lxdev)); 514 memcpy(attr.dev.value, &device, sizeof(device)); 515 len += sizeof(attr.dev); 516 } 517 res = ntfs_set_ntfs_ea(ni, (char*)&attr, len, 0); 518 return (res); 519 } 520