Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

ntfs: add reparse and ea operations

Implement support for Extended Attributes and Reparse Points, enabling
Posix ACL support and, and compatibility with Windows Subsystem for
Linux (WSL) metadata.

Acked-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>

+1515
+942
fs/ntfs/ea.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-or-later 2 + /* 3 + * Pocessing of EA's 4 + * 5 + * Part of this file is based on code from the NTFS-3G. 6 + * 7 + * Copyright (c) 2014-2021 Jean-Pierre Andre 8 + * Copyright (c) 2025 LG Electronics Co., Ltd. 9 + */ 10 + 11 + #include <linux/fs.h> 12 + #include <linux/posix_acl.h> 13 + #include <linux/posix_acl_xattr.h> 14 + #include <linux/xattr.h> 15 + 16 + #include "layout.h" 17 + #include "attrib.h" 18 + #include "index.h" 19 + #include "dir.h" 20 + #include "ea.h" 21 + 22 + static int ntfs_write_ea(struct ntfs_inode *ni, __le32 type, char *value, s64 ea_off, 23 + s64 ea_size, bool need_truncate) 24 + { 25 + struct inode *ea_vi; 26 + int err = 0; 27 + s64 written; 28 + 29 + ea_vi = ntfs_attr_iget(VFS_I(ni), type, AT_UNNAMED, 0); 30 + if (IS_ERR(ea_vi)) 31 + return PTR_ERR(ea_vi); 32 + 33 + written = ntfs_inode_attr_pwrite(ea_vi, ea_off, ea_size, value, false); 34 + if (written != ea_size) 35 + err = -EIO; 36 + else { 37 + struct ntfs_inode *ea_ni = NTFS_I(ea_vi); 38 + 39 + if (need_truncate && ea_ni->data_size > ea_off + ea_size) 40 + ntfs_attr_truncate(ea_ni, ea_off + ea_size); 41 + mark_mft_record_dirty(ni); 42 + } 43 + 44 + iput(ea_vi); 45 + return err; 46 + } 47 + 48 + static int ntfs_ea_lookup(char *ea_buf, s64 ea_buf_size, const char *name, 49 + int name_len, s64 *ea_offset, s64 *ea_size) 50 + { 51 + const struct ea_attr *p_ea; 52 + s64 offset; 53 + unsigned int next; 54 + 55 + if (ea_buf_size < sizeof(struct ea_attr)) 56 + goto out; 57 + 58 + offset = 0; 59 + do { 60 + p_ea = (const struct ea_attr *)&ea_buf[offset]; 61 + next = le32_to_cpu(p_ea->next_entry_offset); 62 + 63 + if (offset + next > ea_buf_size || 64 + ((1 + p_ea->ea_name_length) > (ea_buf_size - offset))) 65 + break; 66 + 67 + if (p_ea->ea_name_length == name_len && 68 + !memcmp(p_ea->ea_name, name, name_len)) { 69 + *ea_offset = offset; 70 + if (next) 71 + *ea_size = next; 72 + else { 73 + unsigned int ea_len = 1 + p_ea->ea_name_length + 74 + le16_to_cpu(p_ea->ea_value_length); 75 + 76 + if ((ea_buf_size - offset) < ea_len) 77 + goto out; 78 + 79 + *ea_size = ALIGN(struct_size(p_ea, ea_name, 80 + 1 + p_ea->ea_name_length + 81 + le16_to_cpu(p_ea->ea_value_length)), 4); 82 + } 83 + 84 + if (ea_buf_size < *ea_offset + *ea_size) 85 + goto out; 86 + 87 + return 0; 88 + } 89 + offset += next; 90 + } while (next > 0 && offset < ea_buf_size && 91 + sizeof(struct ea_attr) < (ea_buf_size - offset)); 92 + 93 + out: 94 + return -ENOENT; 95 + } 96 + 97 + /* 98 + * Return the existing EA 99 + * 100 + * The EA_INFORMATION is not examined and the consistency of the 101 + * existing EA is not checked. 102 + * 103 + * If successful, the full attribute is returned unchanged 104 + * and its size is returned. 105 + * If the designated buffer is too small, the needed size is 106 + * returned, and the buffer is left unchanged. 107 + * If there is an error, a negative value is returned and errno 108 + * is set according to the error. 109 + */ 110 + static int ntfs_get_ea(struct inode *inode, const char *name, size_t name_len, 111 + void *buffer, size_t size) 112 + { 113 + struct ntfs_inode *ni = NTFS_I(inode); 114 + const struct ea_attr *p_ea; 115 + char *ea_buf; 116 + s64 ea_off, ea_size, all_ea_size, ea_info_size; 117 + int err; 118 + u32 ea_info_qlen; 119 + u16 ea_value_len; 120 + struct ea_information *p_ea_info; 121 + 122 + if (!NInoHasEA(ni)) 123 + return -ENODATA; 124 + 125 + p_ea_info = ntfs_attr_readall(ni, AT_EA_INFORMATION, NULL, 0, 126 + &ea_info_size); 127 + if (!p_ea_info || ea_info_size != sizeof(struct ea_information)) { 128 + kvfree(p_ea_info); 129 + return -ENODATA; 130 + } 131 + 132 + ea_info_qlen = le32_to_cpu(p_ea_info->ea_query_length); 133 + kvfree(p_ea_info); 134 + 135 + ea_buf = ntfs_attr_readall(ni, AT_EA, NULL, 0, &all_ea_size); 136 + if (!ea_buf) 137 + return -ENODATA; 138 + 139 + err = ntfs_ea_lookup(ea_buf, ea_info_qlen, name, name_len, &ea_off, 140 + &ea_size); 141 + if (!err) { 142 + p_ea = (struct ea_attr *)&ea_buf[ea_off]; 143 + ea_value_len = le16_to_cpu(p_ea->ea_value_length); 144 + if (!buffer) { 145 + kvfree(ea_buf); 146 + return ea_value_len; 147 + } 148 + 149 + if (ea_value_len > size) { 150 + err = -ERANGE; 151 + goto free_ea_buf; 152 + } 153 + 154 + memcpy(buffer, &p_ea->ea_name[p_ea->ea_name_length + 1], 155 + ea_value_len); 156 + kvfree(ea_buf); 157 + return ea_value_len; 158 + } 159 + 160 + err = -ENODATA; 161 + free_ea_buf: 162 + kvfree(ea_buf); 163 + return err; 164 + } 165 + 166 + static inline int ea_packed_size(const struct ea_attr *p_ea) 167 + { 168 + /* 169 + * 4 bytes for header (flags and lengths) + name length + 1 + 170 + * value length. 171 + */ 172 + return 5 + p_ea->ea_name_length + le16_to_cpu(p_ea->ea_value_length); 173 + } 174 + 175 + /* 176 + * Set a new EA, and set EA_INFORMATION accordingly 177 + * 178 + * This is roughly the same as ZwSetEaFile() on Windows, however 179 + * the "offset to next" of the last EA should not be cleared. 180 + * 181 + * Consistency of the new EA is first checked. 182 + * 183 + * EA_INFORMATION is set first, and it is restored to its former 184 + * state if setting EA fails. 185 + */ 186 + static int ntfs_set_ea(struct inode *inode, const char *name, size_t name_len, 187 + const void *value, size_t val_size, int flags, 188 + __le16 *packed_ea_size) 189 + { 190 + struct ntfs_inode *ni = NTFS_I(inode); 191 + struct ea_information *p_ea_info = NULL; 192 + int ea_packed, err = 0; 193 + struct ea_attr *p_ea; 194 + u32 ea_info_qsize = 0; 195 + char *ea_buf = NULL; 196 + size_t new_ea_size = ALIGN(struct_size(p_ea, ea_name, 1 + name_len + val_size), 4); 197 + s64 ea_off, ea_info_size, all_ea_size, ea_size; 198 + 199 + if (name_len > 255) 200 + return -ENAMETOOLONG; 201 + 202 + if (ntfs_attr_exist(ni, AT_EA_INFORMATION, AT_UNNAMED, 0)) { 203 + p_ea_info = ntfs_attr_readall(ni, AT_EA_INFORMATION, NULL, 0, 204 + &ea_info_size); 205 + if (!p_ea_info || ea_info_size != sizeof(struct ea_information)) 206 + goto out; 207 + 208 + ea_buf = ntfs_attr_readall(ni, AT_EA, NULL, 0, &all_ea_size); 209 + if (!ea_buf) { 210 + ea_info_qsize = 0; 211 + kvfree(p_ea_info); 212 + goto create_ea_info; 213 + } 214 + 215 + ea_info_qsize = le32_to_cpu(p_ea_info->ea_query_length); 216 + } else { 217 + create_ea_info: 218 + p_ea_info = kzalloc(sizeof(struct ea_information), GFP_NOFS); 219 + if (!p_ea_info) 220 + return -ENOMEM; 221 + 222 + ea_info_qsize = 0; 223 + err = ntfs_attr_add(ni, AT_EA_INFORMATION, AT_UNNAMED, 0, 224 + (char *)p_ea_info, sizeof(struct ea_information)); 225 + if (err) 226 + goto out; 227 + 228 + if (ntfs_attr_exist(ni, AT_EA, AT_UNNAMED, 0)) { 229 + err = ntfs_attr_remove(ni, AT_EA, AT_UNNAMED, 0); 230 + if (err) 231 + goto out; 232 + } 233 + 234 + goto alloc_new_ea; 235 + } 236 + 237 + if (ea_info_qsize > all_ea_size) { 238 + err = -EIO; 239 + goto out; 240 + } 241 + 242 + err = ntfs_ea_lookup(ea_buf, ea_info_qsize, name, name_len, &ea_off, 243 + &ea_size); 244 + if (ea_info_qsize && !err) { 245 + if (flags & XATTR_CREATE) { 246 + err = -EEXIST; 247 + goto out; 248 + } 249 + 250 + p_ea = (struct ea_attr *)(ea_buf + ea_off); 251 + 252 + if (val_size && 253 + le16_to_cpu(p_ea->ea_value_length) == val_size && 254 + !memcmp(p_ea->ea_name + p_ea->ea_name_length + 1, value, 255 + val_size)) 256 + goto out; 257 + 258 + le16_add_cpu(&p_ea_info->ea_length, 0 - ea_packed_size(p_ea)); 259 + 260 + if (p_ea->flags & NEED_EA) 261 + le16_add_cpu(&p_ea_info->need_ea_count, -1); 262 + 263 + memmove((char *)p_ea, (char *)p_ea + ea_size, ea_info_qsize - (ea_off + ea_size)); 264 + ea_info_qsize -= ea_size; 265 + p_ea_info->ea_query_length = cpu_to_le32(ea_info_qsize); 266 + 267 + err = ntfs_write_ea(ni, AT_EA_INFORMATION, (char *)p_ea_info, 0, 268 + sizeof(struct ea_information), false); 269 + if (err) 270 + goto out; 271 + 272 + err = ntfs_write_ea(ni, AT_EA, ea_buf, 0, ea_info_qsize, true); 273 + if (err) 274 + goto out; 275 + 276 + if ((flags & XATTR_REPLACE) && !val_size) { 277 + /* Remove xattr. */ 278 + goto out; 279 + } 280 + } else { 281 + if (flags & XATTR_REPLACE) { 282 + err = -ENODATA; 283 + goto out; 284 + } 285 + } 286 + kvfree(ea_buf); 287 + 288 + alloc_new_ea: 289 + ea_buf = kzalloc(new_ea_size, GFP_NOFS); 290 + if (!ea_buf) { 291 + err = -ENOMEM; 292 + goto out; 293 + } 294 + 295 + /* 296 + * EA and REPARSE_POINT compatibility not checked any more, 297 + * required by Windows 10, but having both may lead to 298 + * problems with earlier versions. 299 + */ 300 + p_ea = (struct ea_attr *)ea_buf; 301 + memcpy(p_ea->ea_name, name, name_len); 302 + p_ea->ea_name_length = name_len; 303 + p_ea->ea_name[name_len] = 0; 304 + memcpy(p_ea->ea_name + name_len + 1, value, val_size); 305 + p_ea->ea_value_length = cpu_to_le16(val_size); 306 + p_ea->next_entry_offset = cpu_to_le32(new_ea_size); 307 + 308 + ea_packed = le16_to_cpu(p_ea_info->ea_length) + ea_packed_size(p_ea); 309 + p_ea_info->ea_length = cpu_to_le16(ea_packed); 310 + p_ea_info->ea_query_length = cpu_to_le32(ea_info_qsize + new_ea_size); 311 + 312 + if (ea_packed > 0xffff || 313 + ntfs_attr_size_bounds_check(ni->vol, AT_EA, new_ea_size)) { 314 + err = -EFBIG; 315 + goto out; 316 + } 317 + 318 + /* 319 + * no EA or EA_INFORMATION : add them 320 + */ 321 + if (!ntfs_attr_exist(ni, AT_EA, AT_UNNAMED, 0)) { 322 + err = ntfs_attr_add(ni, AT_EA, AT_UNNAMED, 0, (char *)p_ea, 323 + new_ea_size); 324 + if (err) 325 + goto out; 326 + } else { 327 + err = ntfs_write_ea(ni, AT_EA, (char *)p_ea, ea_info_qsize, 328 + new_ea_size, false); 329 + if (err) 330 + goto out; 331 + } 332 + 333 + err = ntfs_write_ea(ni, AT_EA_INFORMATION, (char *)p_ea_info, 0, 334 + sizeof(struct ea_information), false); 335 + if (err) 336 + goto out; 337 + 338 + if (packed_ea_size) 339 + *packed_ea_size = p_ea_info->ea_length; 340 + mark_mft_record_dirty(ni); 341 + out: 342 + if (ea_info_qsize > 0) 343 + NInoSetHasEA(ni); 344 + else 345 + NInoClearHasEA(ni); 346 + 347 + kvfree(ea_buf); 348 + kvfree(p_ea_info); 349 + 350 + return err; 351 + } 352 + 353 + /* 354 + * Check for the presence of an EA "$LXDEV" (used by WSL) 355 + * and return its value as a device address 356 + */ 357 + int ntfs_ea_get_wsl_inode(struct inode *inode, dev_t *rdevp, unsigned int flags) 358 + { 359 + int err; 360 + __le32 v; 361 + 362 + if (!(flags & NTFS_VOL_UID)) { 363 + /* Load uid to lxuid EA */ 364 + err = ntfs_get_ea(inode, "$LXUID", sizeof("$LXUID") - 1, &v, 365 + sizeof(v)); 366 + if (err < 0) 367 + return err; 368 + i_uid_write(inode, le32_to_cpu(v)); 369 + } 370 + 371 + if (!(flags & NTFS_VOL_UID)) { 372 + /* Load gid to lxgid EA */ 373 + err = ntfs_get_ea(inode, "$LXGID", sizeof("$LXGID") - 1, &v, 374 + sizeof(v)); 375 + if (err < 0) 376 + return err; 377 + i_gid_write(inode, le32_to_cpu(v)); 378 + } 379 + 380 + /* Load mode to lxmod EA */ 381 + err = ntfs_get_ea(inode, "$LXMOD", sizeof("$LXMOD") - 1, &v, sizeof(v)); 382 + if (err > 0) { 383 + inode->i_mode = le32_to_cpu(v); 384 + } else { 385 + /* Everyone gets all permissions. */ 386 + inode->i_mode |= 0777; 387 + } 388 + 389 + /* Load mode to lxdev EA */ 390 + err = ntfs_get_ea(inode, "$LXDEV", sizeof("$LXDEV") - 1, &v, sizeof(v)); 391 + if (err > 0) 392 + *rdevp = le32_to_cpu(v); 393 + err = 0; 394 + 395 + return err; 396 + } 397 + 398 + int ntfs_ea_set_wsl_inode(struct inode *inode, dev_t rdev, __le16 *ea_size, 399 + unsigned int flags) 400 + { 401 + __le32 v; 402 + int err; 403 + 404 + if (flags & NTFS_EA_UID) { 405 + /* Store uid to lxuid EA */ 406 + v = cpu_to_le32(i_uid_read(inode)); 407 + err = ntfs_set_ea(inode, "$LXUID", sizeof("$LXUID") - 1, &v, 408 + sizeof(v), 0, ea_size); 409 + if (err) 410 + return err; 411 + } 412 + 413 + if (flags & NTFS_EA_GID) { 414 + /* Store gid to lxgid EA */ 415 + v = cpu_to_le32(i_gid_read(inode)); 416 + err = ntfs_set_ea(inode, "$LXGID", sizeof("$LXGID") - 1, &v, 417 + sizeof(v), 0, ea_size); 418 + if (err) 419 + return err; 420 + } 421 + 422 + if (flags & NTFS_EA_MODE) { 423 + /* Store mode to lxmod EA */ 424 + v = cpu_to_le32(inode->i_mode); 425 + err = ntfs_set_ea(inode, "$LXMOD", sizeof("$LXMOD") - 1, &v, 426 + sizeof(v), 0, ea_size); 427 + if (err) 428 + return err; 429 + } 430 + 431 + if (rdev) { 432 + v = cpu_to_le32(rdev); 433 + err = ntfs_set_ea(inode, "$LXDEV", sizeof("$LXDEV") - 1, &v, sizeof(v), 434 + 0, ea_size); 435 + } 436 + 437 + return err; 438 + } 439 + 440 + ssize_t ntfs_listxattr(struct dentry *dentry, char *buffer, size_t size) 441 + { 442 + struct inode *inode = d_inode(dentry); 443 + struct ntfs_inode *ni = NTFS_I(inode); 444 + const struct ea_attr *p_ea; 445 + s64 offset, ea_buf_size, ea_info_size; 446 + int next, err = 0, ea_size; 447 + u32 ea_info_qsize; 448 + char *ea_buf = NULL; 449 + ssize_t ret = 0; 450 + struct ea_information *ea_info; 451 + 452 + if (!NInoHasEA(ni)) 453 + return 0; 454 + 455 + mutex_lock(&NTFS_I(inode)->mrec_lock); 456 + ea_info = ntfs_attr_readall(ni, AT_EA_INFORMATION, NULL, 0, 457 + &ea_info_size); 458 + if (!ea_info || ea_info_size != sizeof(struct ea_information)) 459 + goto out; 460 + 461 + ea_info_qsize = le32_to_cpu(ea_info->ea_query_length); 462 + 463 + ea_buf = ntfs_attr_readall(ni, AT_EA, NULL, 0, &ea_buf_size); 464 + if (!ea_buf) 465 + goto out; 466 + 467 + if (ea_info_qsize > ea_buf_size) 468 + goto out; 469 + 470 + if (ea_buf_size < sizeof(struct ea_attr)) 471 + goto out; 472 + 473 + offset = 0; 474 + do { 475 + p_ea = (const struct ea_attr *)&ea_buf[offset]; 476 + next = le32_to_cpu(p_ea->next_entry_offset); 477 + if (next) 478 + ea_size = next; 479 + else 480 + ea_size = ALIGN(struct_size(p_ea, ea_name, 481 + 1 + p_ea->ea_name_length + 482 + le16_to_cpu(p_ea->ea_value_length)), 483 + 4); 484 + if (buffer) { 485 + if (offset + ea_size > ea_info_qsize) 486 + break; 487 + 488 + if (ret + p_ea->ea_name_length + 1 > size) { 489 + err = -ERANGE; 490 + goto out; 491 + } 492 + 493 + if (p_ea->ea_name_length + 1 > (ea_info_qsize - offset)) 494 + break; 495 + 496 + memcpy(buffer + ret, p_ea->ea_name, p_ea->ea_name_length); 497 + buffer[ret + p_ea->ea_name_length] = 0; 498 + } 499 + 500 + ret += p_ea->ea_name_length + 1; 501 + offset += ea_size; 502 + } while (next > 0 && offset < ea_info_qsize && 503 + sizeof(struct ea_attr) < (ea_info_qsize - offset)); 504 + 505 + out: 506 + mutex_unlock(&NTFS_I(inode)->mrec_lock); 507 + kvfree(ea_info); 508 + kvfree(ea_buf); 509 + 510 + return err ? err : ret; 511 + } 512 + 513 + // clang-format off 514 + #define SYSTEM_DOS_ATTRIB "system.dos_attrib" 515 + #define SYSTEM_NTFS_ATTRIB "system.ntfs_attrib" 516 + #define SYSTEM_NTFS_ATTRIB_BE "system.ntfs_attrib_be" 517 + // clang-format on 518 + 519 + static int ntfs_getxattr(const struct xattr_handler *handler, 520 + struct dentry *unused, struct inode *inode, const char *name, 521 + void *buffer, size_t size) 522 + { 523 + struct ntfs_inode *ni = NTFS_I(inode); 524 + int err; 525 + 526 + if (NVolShutdown(ni->vol)) 527 + return -EIO; 528 + 529 + if (!strcmp(name, SYSTEM_DOS_ATTRIB)) { 530 + if (!buffer) { 531 + err = sizeof(u8); 532 + } else if (size < sizeof(u8)) { 533 + err = -ENODATA; 534 + } else { 535 + err = sizeof(u8); 536 + *(u8 *)buffer = (u8)(le32_to_cpu(ni->flags) & 0x3F); 537 + } 538 + goto out; 539 + } 540 + 541 + if (!strcmp(name, SYSTEM_NTFS_ATTRIB) || 542 + !strcmp(name, SYSTEM_NTFS_ATTRIB_BE)) { 543 + if (!buffer) { 544 + err = sizeof(u32); 545 + } else if (size < sizeof(u32)) { 546 + err = -ENODATA; 547 + } else { 548 + err = sizeof(u32); 549 + *(u32 *)buffer = le32_to_cpu(ni->flags); 550 + if (!strcmp(name, SYSTEM_NTFS_ATTRIB_BE)) 551 + *(__be32 *)buffer = cpu_to_be32(*(u32 *)buffer); 552 + } 553 + goto out; 554 + } 555 + 556 + mutex_lock(&ni->mrec_lock); 557 + err = ntfs_get_ea(inode, name, strlen(name), buffer, size); 558 + mutex_unlock(&ni->mrec_lock); 559 + 560 + out: 561 + return err; 562 + } 563 + 564 + static int ntfs_new_attr_flags(struct ntfs_inode *ni, __le32 fattr) 565 + { 566 + struct ntfs_attr_search_ctx *ctx; 567 + struct mft_record *m; 568 + struct attr_record *a; 569 + __le16 new_aflags; 570 + int mp_size, mp_ofs, name_ofs, arec_size, err; 571 + 572 + m = map_mft_record(ni); 573 + if (IS_ERR(m)) 574 + return PTR_ERR(m); 575 + 576 + ctx = ntfs_attr_get_search_ctx(ni, m); 577 + if (!ctx) { 578 + err = -ENOMEM; 579 + goto err_out; 580 + } 581 + 582 + err = ntfs_attr_lookup(ni->type, ni->name, ni->name_len, 583 + CASE_SENSITIVE, 0, NULL, 0, ctx); 584 + if (err) { 585 + err = -EINVAL; 586 + goto err_out; 587 + } 588 + 589 + a = ctx->attr; 590 + new_aflags = ctx->attr->flags; 591 + 592 + if (fattr & FILE_ATTR_SPARSE_FILE) 593 + new_aflags |= ATTR_IS_SPARSE; 594 + else 595 + new_aflags &= ~ATTR_IS_SPARSE; 596 + 597 + if (fattr & FILE_ATTR_COMPRESSED) 598 + new_aflags |= ATTR_IS_COMPRESSED; 599 + else 600 + new_aflags &= ~ATTR_IS_COMPRESSED; 601 + 602 + if (new_aflags == a->flags) 603 + return 0; 604 + 605 + if ((new_aflags & (ATTR_IS_SPARSE | ATTR_IS_COMPRESSED)) == 606 + (ATTR_IS_SPARSE | ATTR_IS_COMPRESSED)) { 607 + pr_err("file can't be sparsed and compressed\n"); 608 + err = -EOPNOTSUPP; 609 + goto err_out; 610 + } 611 + 612 + if (!a->non_resident) 613 + goto out; 614 + 615 + if (a->data.non_resident.data_size) { 616 + pr_err("Can't change sparsed/compressed for non-empty file"); 617 + err = -EOPNOTSUPP; 618 + goto err_out; 619 + } 620 + 621 + if (new_aflags & (ATTR_IS_SPARSE | ATTR_IS_COMPRESSED)) 622 + name_ofs = (offsetof(struct attr_record, 623 + data.non_resident.compressed_size) + 624 + sizeof(a->data.non_resident.compressed_size) + 7) & ~7; 625 + else 626 + name_ofs = (offsetof(struct attr_record, 627 + data.non_resident.compressed_size) + 7) & ~7; 628 + 629 + mp_size = ntfs_get_size_for_mapping_pairs(ni->vol, ni->runlist.rl, 0, -1, -1); 630 + if (unlikely(mp_size < 0)) { 631 + err = mp_size; 632 + ntfs_debug("Failed to get size for mapping pairs array, error code %i.\n", err); 633 + goto err_out; 634 + } 635 + 636 + mp_ofs = (name_ofs + a->name_length * sizeof(__le16) + 7) & ~7; 637 + arec_size = (mp_ofs + mp_size + 7) & ~7; 638 + 639 + err = ntfs_attr_record_resize(m, a, arec_size); 640 + if (unlikely(err)) 641 + goto err_out; 642 + 643 + if (new_aflags & (ATTR_IS_SPARSE | ATTR_IS_COMPRESSED)) { 644 + a->data.non_resident.compression_unit = 0; 645 + if (new_aflags & ATTR_IS_COMPRESSED || ni->vol->major_ver < 3) 646 + a->data.non_resident.compression_unit = 4; 647 + a->data.non_resident.compressed_size = 0; 648 + ni->itype.compressed.size = 0; 649 + if (a->data.non_resident.compression_unit) { 650 + ni->itype.compressed.block_size = 1U << 651 + (a->data.non_resident.compression_unit + 652 + ni->vol->cluster_size_bits); 653 + ni->itype.compressed.block_size_bits = 654 + ffs(ni->itype.compressed.block_size) - 655 + 1; 656 + ni->itype.compressed.block_clusters = 1U << 657 + a->data.non_resident.compression_unit; 658 + } else { 659 + ni->itype.compressed.block_size = 0; 660 + ni->itype.compressed.block_size_bits = 0; 661 + ni->itype.compressed.block_clusters = 0; 662 + } 663 + 664 + if (new_aflags & ATTR_IS_SPARSE) { 665 + NInoSetSparse(ni); 666 + ni->flags |= FILE_ATTR_SPARSE_FILE; 667 + } 668 + 669 + if (new_aflags & ATTR_IS_COMPRESSED) { 670 + NInoSetCompressed(ni); 671 + ni->flags |= FILE_ATTR_COMPRESSED; 672 + } 673 + } else { 674 + ni->flags &= ~(FILE_ATTR_SPARSE_FILE | FILE_ATTR_COMPRESSED); 675 + a->data.non_resident.compression_unit = 0; 676 + NInoClearSparse(ni); 677 + NInoClearCompressed(ni); 678 + } 679 + 680 + a->name_offset = cpu_to_le16(name_ofs); 681 + a->data.non_resident.mapping_pairs_offset = cpu_to_le16(mp_ofs); 682 + 683 + out: 684 + a->flags = new_aflags; 685 + mark_mft_record_dirty(ctx->ntfs_ino); 686 + err_out: 687 + ntfs_attr_put_search_ctx(ctx); 688 + unmap_mft_record(ni); 689 + return err; 690 + } 691 + 692 + static int ntfs_setxattr(const struct xattr_handler *handler, 693 + struct mnt_idmap *idmap, struct dentry *unused, 694 + struct inode *inode, const char *name, const void *value, 695 + size_t size, int flags) 696 + { 697 + struct ntfs_inode *ni = NTFS_I(inode); 698 + int err; 699 + __le32 fattr; 700 + 701 + if (NVolShutdown(ni->vol)) 702 + return -EIO; 703 + 704 + if (!strcmp(name, SYSTEM_DOS_ATTRIB)) { 705 + if (sizeof(u8) != size) { 706 + err = -EINVAL; 707 + goto out; 708 + } 709 + fattr = cpu_to_le32(*(u8 *)value); 710 + goto set_fattr; 711 + } 712 + 713 + if (!strcmp(name, SYSTEM_NTFS_ATTRIB) || 714 + !strcmp(name, SYSTEM_NTFS_ATTRIB_BE)) { 715 + if (size != sizeof(u32)) { 716 + err = -EINVAL; 717 + goto out; 718 + } 719 + if (!strcmp(name, SYSTEM_NTFS_ATTRIB_BE)) 720 + fattr = cpu_to_le32(be32_to_cpu(*(__be32 *)value)); 721 + else 722 + fattr = cpu_to_le32(*(u32 *)value); 723 + 724 + if (S_ISREG(inode->i_mode)) { 725 + mutex_lock(&ni->mrec_lock); 726 + err = ntfs_new_attr_flags(ni, fattr); 727 + mutex_unlock(&ni->mrec_lock); 728 + if (err) 729 + goto out; 730 + } 731 + 732 + set_fattr: 733 + if (S_ISDIR(inode->i_mode)) 734 + fattr |= FILE_ATTR_DIRECTORY; 735 + else 736 + fattr &= ~FILE_ATTR_DIRECTORY; 737 + 738 + if (ni->flags != fattr) { 739 + ni->flags = fattr; 740 + if (fattr & FILE_ATTR_READONLY) 741 + inode->i_mode &= ~0222; 742 + else 743 + inode->i_mode |= 0222; 744 + NInoSetFileNameDirty(ni); 745 + mark_inode_dirty(inode); 746 + } 747 + err = 0; 748 + goto out; 749 + } 750 + 751 + mutex_lock(&ni->mrec_lock); 752 + err = ntfs_set_ea(inode, name, strlen(name), value, size, flags, NULL); 753 + mutex_unlock(&ni->mrec_lock); 754 + 755 + out: 756 + inode_set_ctime_current(inode); 757 + mark_inode_dirty(inode); 758 + return err; 759 + } 760 + 761 + static bool ntfs_xattr_user_list(struct dentry *dentry) 762 + { 763 + return true; 764 + } 765 + 766 + // clang-format off 767 + static const struct xattr_handler ntfs_other_xattr_handler = { 768 + .prefix = "", 769 + .get = ntfs_getxattr, 770 + .set = ntfs_setxattr, 771 + .list = ntfs_xattr_user_list, 772 + }; 773 + 774 + const struct xattr_handler * const ntfs_xattr_handlers[] = { 775 + &ntfs_other_xattr_handler, 776 + NULL, 777 + }; 778 + // clang-format on 779 + 780 + #ifdef CONFIG_NTFS_FS_POSIX_ACL 781 + struct posix_acl *ntfs_get_acl(struct mnt_idmap *idmap, struct dentry *dentry, 782 + int type) 783 + { 784 + struct inode *inode = d_inode(dentry); 785 + struct ntfs_inode *ni = NTFS_I(inode); 786 + const char *name; 787 + size_t name_len; 788 + struct posix_acl *acl; 789 + int err; 790 + void *buf; 791 + 792 + buf = kmalloc(PATH_MAX, GFP_KERNEL); 793 + if (!buf) 794 + return ERR_PTR(-ENOMEM); 795 + 796 + /* Possible values of 'type' was already checked above. */ 797 + if (type == ACL_TYPE_ACCESS) { 798 + name = XATTR_NAME_POSIX_ACL_ACCESS; 799 + name_len = sizeof(XATTR_NAME_POSIX_ACL_ACCESS) - 1; 800 + } else { 801 + name = XATTR_NAME_POSIX_ACL_DEFAULT; 802 + name_len = sizeof(XATTR_NAME_POSIX_ACL_DEFAULT) - 1; 803 + } 804 + 805 + mutex_lock(&ni->mrec_lock); 806 + err = ntfs_get_ea(inode, name, name_len, buf, PATH_MAX); 807 + mutex_unlock(&ni->mrec_lock); 808 + 809 + /* Translate extended attribute to acl. */ 810 + if (err >= 0) 811 + acl = posix_acl_from_xattr(&init_user_ns, buf, err); 812 + else if (err == -ENODATA) 813 + acl = NULL; 814 + else 815 + acl = ERR_PTR(err); 816 + 817 + if (!IS_ERR(acl)) 818 + set_cached_acl(inode, type, acl); 819 + 820 + kfree(buf); 821 + 822 + return acl; 823 + } 824 + 825 + static noinline int ntfs_set_acl_ex(struct mnt_idmap *idmap, 826 + struct inode *inode, struct posix_acl *acl, 827 + int type, bool init_acl) 828 + { 829 + const char *name; 830 + size_t size, name_len; 831 + void *value; 832 + int err; 833 + int flags; 834 + umode_t mode; 835 + 836 + if (S_ISLNK(inode->i_mode)) 837 + return -EOPNOTSUPP; 838 + 839 + mode = inode->i_mode; 840 + switch (type) { 841 + case ACL_TYPE_ACCESS: 842 + /* Do not change i_mode if we are in init_acl */ 843 + if (acl && !init_acl) { 844 + err = posix_acl_update_mode(idmap, inode, &mode, &acl); 845 + if (err) 846 + return err; 847 + } 848 + name = XATTR_NAME_POSIX_ACL_ACCESS; 849 + name_len = sizeof(XATTR_NAME_POSIX_ACL_ACCESS) - 1; 850 + break; 851 + 852 + case ACL_TYPE_DEFAULT: 853 + if (!S_ISDIR(inode->i_mode)) 854 + return acl ? -EACCES : 0; 855 + name = XATTR_NAME_POSIX_ACL_DEFAULT; 856 + name_len = sizeof(XATTR_NAME_POSIX_ACL_DEFAULT) - 1; 857 + break; 858 + 859 + default: 860 + return -EINVAL; 861 + } 862 + 863 + if (!acl) { 864 + /* Remove xattr if it can be presented via mode. */ 865 + size = 0; 866 + value = NULL; 867 + flags = XATTR_REPLACE; 868 + } else { 869 + value = posix_acl_to_xattr(&init_user_ns, acl, &size, GFP_NOFS); 870 + if (!value) 871 + return -ENOMEM; 872 + flags = 0; 873 + } 874 + 875 + mutex_lock(&NTFS_I(inode)->mrec_lock); 876 + err = ntfs_set_ea(inode, name, name_len, value, size, flags, NULL); 877 + mutex_unlock(&NTFS_I(inode)->mrec_lock); 878 + if (err == -ENODATA && !size) 879 + err = 0; /* Removing non existed xattr. */ 880 + if (!err) { 881 + __le16 ea_size = 0; 882 + umode_t old_mode = inode->i_mode; 883 + 884 + inode->i_mode = mode; 885 + mutex_lock(&NTFS_I(inode)->mrec_lock); 886 + err = ntfs_ea_set_wsl_inode(inode, 0, &ea_size, NTFS_EA_MODE); 887 + if (err) { 888 + ntfs_set_ea(inode, name, name_len, NULL, 0, 889 + XATTR_REPLACE, NULL); 890 + mutex_unlock(&NTFS_I(inode)->mrec_lock); 891 + inode->i_mode = old_mode; 892 + goto out; 893 + } 894 + mutex_unlock(&NTFS_I(inode)->mrec_lock); 895 + 896 + set_cached_acl(inode, type, acl); 897 + inode_set_ctime_current(inode); 898 + mark_inode_dirty(inode); 899 + } 900 + 901 + out: 902 + kfree(value); 903 + 904 + return err; 905 + } 906 + 907 + int ntfs_set_acl(struct mnt_idmap *idmap, struct dentry *dentry, 908 + struct posix_acl *acl, int type) 909 + { 910 + return ntfs_set_acl_ex(idmap, d_inode(dentry), acl, type, false); 911 + } 912 + 913 + int ntfs_init_acl(struct mnt_idmap *idmap, struct inode *inode, 914 + struct inode *dir) 915 + { 916 + struct posix_acl *default_acl, *acl; 917 + int err; 918 + 919 + err = posix_acl_create(dir, &inode->i_mode, &default_acl, &acl); 920 + if (err) 921 + return err; 922 + 923 + if (default_acl) { 924 + err = ntfs_set_acl_ex(idmap, inode, default_acl, 925 + ACL_TYPE_DEFAULT, true); 926 + posix_acl_release(default_acl); 927 + } else { 928 + inode->i_default_acl = NULL; 929 + } 930 + 931 + if (acl) { 932 + if (!err) 933 + err = ntfs_set_acl_ex(idmap, inode, acl, 934 + ACL_TYPE_ACCESS, true); 935 + posix_acl_release(acl); 936 + } else { 937 + inode->i_acl = NULL; 938 + } 939 + 940 + return err; 941 + } 942 + #endif
+573
fs/ntfs/reparse.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-or-later 2 + /* 3 + * Processing of reparse points 4 + * 5 + * Part of this file is based on code from the NTFS-3G. 6 + * 7 + * Copyright (c) 2008-2021 Jean-Pierre Andre 8 + * Copyright (c) 2025 LG Electronics Co., Ltd. 9 + */ 10 + 11 + #include "ntfs.h" 12 + #include "layout.h" 13 + #include "attrib.h" 14 + #include "inode.h" 15 + #include "dir.h" 16 + #include "volume.h" 17 + #include "mft.h" 18 + #include "index.h" 19 + #include "lcnalloc.h" 20 + #include "reparse.h" 21 + 22 + struct wsl_link_reparse_data { 23 + __le32 type; 24 + char link[]; 25 + }; 26 + 27 + /* Index entry in $Extend/$Reparse */ 28 + struct reparse_index { 29 + struct index_entry_header header; 30 + struct reparse_index_key key; 31 + __le32 filling; 32 + }; 33 + 34 + __le16 reparse_index_name[] = {cpu_to_le16('$'), cpu_to_le16('R'), 0}; 35 + 36 + 37 + /* 38 + * Check if the reparse point attribute buffer is valid. 39 + * Returns true if valid, false otherwise. 40 + */ 41 + static bool ntfs_is_valid_reparse_buffer(struct ntfs_inode *ni, 42 + const struct reparse_point *reparse_attr, size_t size) 43 + { 44 + size_t expected; 45 + 46 + if (!ni || !reparse_attr) 47 + return false; 48 + 49 + /* Minimum size must cover reparse_point header */ 50 + if (size < sizeof(struct reparse_point)) 51 + return false; 52 + 53 + /* Reserved zero tag is invalid */ 54 + if (reparse_attr->reparse_tag == IO_REPARSE_TAG_RESERVED_ZERO) 55 + return false; 56 + 57 + /* Calculate expected total size */ 58 + expected = sizeof(struct reparse_point) + 59 + le16_to_cpu(reparse_attr->reparse_data_length); 60 + 61 + /* Add GUID size for non-Microsoft tags */ 62 + if (!(reparse_attr->reparse_tag & IO_REPARSE_TAG_IS_MICROSOFT)) 63 + expected += sizeof(struct guid); 64 + 65 + /* Buffer must exactly match the expected size */ 66 + return expected == size; 67 + } 68 + 69 + /* 70 + * Do some sanity checks on reparse data 71 + * 72 + * Microsoft reparse points have an 8-byte header whereas 73 + * non-Microsoft reparse points have a 24-byte header. In each case, 74 + * 'reparse_data_length' must equal the number of non-header bytes. 75 + * 76 + * If the reparse data looks like a junction point or symbolic 77 + * link, more checks can be done. 78 + */ 79 + static bool valid_reparse_data(struct ntfs_inode *ni, 80 + const struct reparse_point *reparse_attr, size_t size) 81 + { 82 + const struct wsl_link_reparse_data *wsl_reparse_data = 83 + (const struct wsl_link_reparse_data *)reparse_attr->reparse_data; 84 + unsigned int data_len = le16_to_cpu(reparse_attr->reparse_data_length); 85 + 86 + if (ntfs_is_valid_reparse_buffer(ni, reparse_attr, size) == false) 87 + return false; 88 + 89 + switch (reparse_attr->reparse_tag) { 90 + case IO_REPARSE_TAG_LX_SYMLINK: 91 + if (data_len <= sizeof(wsl_reparse_data->type) || 92 + wsl_reparse_data->type != cpu_to_le32(2)) 93 + return false; 94 + break; 95 + case IO_REPARSE_TAG_AF_UNIX: 96 + case IO_REPARSE_TAG_LX_FIFO: 97 + case IO_REPARSE_TAG_LX_CHR: 98 + case IO_REPARSE_TAG_LX_BLK: 99 + if (data_len || !(ni->flags & FILE_ATTRIBUTE_RECALL_ON_OPEN)) 100 + return false; 101 + } 102 + 103 + return true; 104 + } 105 + 106 + static unsigned int ntfs_reparse_tag_mode(struct reparse_point *reparse_attr) 107 + { 108 + unsigned int mode = 0; 109 + 110 + switch (reparse_attr->reparse_tag) { 111 + case IO_REPARSE_TAG_SYMLINK: 112 + case IO_REPARSE_TAG_LX_SYMLINK: 113 + mode = S_IFLNK; 114 + break; 115 + case IO_REPARSE_TAG_AF_UNIX: 116 + mode = S_IFSOCK; 117 + break; 118 + case IO_REPARSE_TAG_LX_FIFO: 119 + mode = S_IFIFO; 120 + break; 121 + case IO_REPARSE_TAG_LX_CHR: 122 + mode = S_IFCHR; 123 + break; 124 + case IO_REPARSE_TAG_LX_BLK: 125 + mode = S_IFBLK; 126 + } 127 + 128 + return mode; 129 + } 130 + 131 + /* 132 + * Get the target for symbolic link 133 + */ 134 + unsigned int ntfs_make_symlink(struct ntfs_inode *ni) 135 + { 136 + s64 attr_size = 0; 137 + unsigned int lth; 138 + struct reparse_point *reparse_attr; 139 + struct wsl_link_reparse_data *wsl_link_data; 140 + unsigned int mode = 0; 141 + 142 + reparse_attr = ntfs_attr_readall(ni, AT_REPARSE_POINT, NULL, 0, 143 + &attr_size); 144 + if (reparse_attr && attr_size && 145 + valid_reparse_data(ni, reparse_attr, attr_size)) { 146 + switch (reparse_attr->reparse_tag) { 147 + case IO_REPARSE_TAG_LX_SYMLINK: 148 + wsl_link_data = 149 + (struct wsl_link_reparse_data *)reparse_attr->reparse_data; 150 + if (wsl_link_data->type == cpu_to_le32(2)) { 151 + lth = le16_to_cpu(reparse_attr->reparse_data_length) - 152 + sizeof(wsl_link_data->type); 153 + ni->target = kvzalloc(lth + 1, GFP_NOFS); 154 + if (ni->target) { 155 + memcpy(ni->target, wsl_link_data->link, lth); 156 + ni->target[lth] = 0; 157 + mode = ntfs_reparse_tag_mode(reparse_attr); 158 + } 159 + } 160 + break; 161 + default: 162 + mode = ntfs_reparse_tag_mode(reparse_attr); 163 + } 164 + } else 165 + ni->flags &= ~FILE_ATTR_REPARSE_POINT; 166 + 167 + if (reparse_attr) 168 + kvfree(reparse_attr); 169 + 170 + return mode; 171 + } 172 + 173 + unsigned int ntfs_reparse_tag_dt_types(struct ntfs_volume *vol, unsigned long mref) 174 + { 175 + s64 attr_size = 0; 176 + struct reparse_point *reparse_attr; 177 + unsigned int dt_type = DT_UNKNOWN; 178 + struct inode *vi; 179 + 180 + vi = ntfs_iget(vol->sb, mref); 181 + if (IS_ERR(vi)) 182 + return PTR_ERR(vi); 183 + 184 + reparse_attr = (struct reparse_point *)ntfs_attr_readall(NTFS_I(vi), 185 + AT_REPARSE_POINT, NULL, 0, &attr_size); 186 + 187 + if (reparse_attr && attr_size) { 188 + switch (reparse_attr->reparse_tag) { 189 + case IO_REPARSE_TAG_SYMLINK: 190 + case IO_REPARSE_TAG_LX_SYMLINK: 191 + dt_type = DT_LNK; 192 + break; 193 + case IO_REPARSE_TAG_AF_UNIX: 194 + dt_type = DT_SOCK; 195 + break; 196 + case IO_REPARSE_TAG_LX_FIFO: 197 + dt_type = DT_FIFO; 198 + break; 199 + case IO_REPARSE_TAG_LX_CHR: 200 + dt_type = DT_CHR; 201 + break; 202 + case IO_REPARSE_TAG_LX_BLK: 203 + dt_type = DT_BLK; 204 + } 205 + } 206 + 207 + if (reparse_attr) 208 + kvfree(reparse_attr); 209 + 210 + iput(vi); 211 + return dt_type; 212 + } 213 + 214 + /* 215 + * Set the index for new reparse data 216 + */ 217 + static int set_reparse_index(struct ntfs_inode *ni, struct ntfs_index_context *xr, 218 + __le32 reparse_tag) 219 + { 220 + struct reparse_index indx; 221 + u64 file_id_cpu; 222 + __le64 file_id; 223 + 224 + file_id_cpu = MK_MREF(ni->mft_no, ni->seq_no); 225 + file_id = cpu_to_le64(file_id_cpu); 226 + indx.header.data.vi.data_offset = 227 + cpu_to_le16(sizeof(struct index_entry_header) + sizeof(struct reparse_index_key)); 228 + indx.header.data.vi.data_length = 0; 229 + indx.header.data.vi.reservedV = 0; 230 + indx.header.length = cpu_to_le16(sizeof(struct reparse_index)); 231 + indx.header.key_length = cpu_to_le16(sizeof(struct reparse_index_key)); 232 + indx.header.flags = 0; 233 + indx.header.reserved = 0; 234 + indx.key.reparse_tag = reparse_tag; 235 + /* danger on processors which require proper alignment! */ 236 + memcpy(&indx.key.file_id, &file_id, 8); 237 + indx.filling = 0; 238 + ntfs_index_ctx_reinit(xr); 239 + 240 + return ntfs_ie_add(xr, (struct index_entry *)&indx); 241 + } 242 + 243 + /* 244 + * Remove a reparse data index entry if attribute present 245 + */ 246 + static int remove_reparse_index(struct inode *rp, struct ntfs_index_context *xr, 247 + __le32 *preparse_tag) 248 + { 249 + struct reparse_index_key key; 250 + u64 file_id_cpu; 251 + __le64 file_id; 252 + s64 size; 253 + struct ntfs_inode *ni = NTFS_I(rp); 254 + int err = 0, ret = ni->data_size; 255 + 256 + if (ni->data_size == 0) 257 + return 0; 258 + 259 + /* read the existing reparse_tag */ 260 + size = ntfs_inode_attr_pread(rp, 0, 4, (char *)preparse_tag); 261 + if (size != 4) 262 + return -ENODATA; 263 + 264 + file_id_cpu = MK_MREF(ni->mft_no, ni->seq_no); 265 + file_id = cpu_to_le64(file_id_cpu); 266 + key.reparse_tag = *preparse_tag; 267 + /* danger on processors which require proper alignment! */ 268 + memcpy(&key.file_id, &file_id, 8); 269 + if (!ntfs_index_lookup(&key, sizeof(struct reparse_index_key), xr)) { 270 + err = ntfs_index_rm(xr); 271 + if (err) 272 + ret = err; 273 + } 274 + return ret; 275 + } 276 + 277 + /* 278 + * Open the $Extend/$Reparse file and its index 279 + */ 280 + static struct ntfs_index_context *open_reparse_index(struct ntfs_volume *vol) 281 + { 282 + struct ntfs_index_context *xr = NULL; 283 + u64 mref; 284 + __le16 *uname; 285 + struct ntfs_name *name = NULL; 286 + int uname_len; 287 + struct inode *vi, *dir_vi; 288 + 289 + /* do not use path_name_to inode - could reopen root */ 290 + dir_vi = ntfs_iget(vol->sb, FILE_Extend); 291 + if (IS_ERR(dir_vi)) 292 + return NULL; 293 + 294 + uname_len = ntfs_nlstoucs(vol, "$Reparse", 8, &uname, 295 + NTFS_MAX_NAME_LEN); 296 + if (uname_len < 0) { 297 + iput(dir_vi); 298 + return NULL; 299 + } 300 + 301 + mutex_lock_nested(&NTFS_I(dir_vi)->mrec_lock, NTFS_EXTEND_MUTEX_PARENT); 302 + mref = ntfs_lookup_inode_by_name(NTFS_I(dir_vi), uname, uname_len, 303 + &name); 304 + mutex_unlock(&NTFS_I(dir_vi)->mrec_lock); 305 + kfree(name); 306 + kmem_cache_free(ntfs_name_cache, uname); 307 + if (IS_ERR_MREF(mref)) 308 + goto put_dir_vi; 309 + 310 + vi = ntfs_iget(vol->sb, MREF(mref)); 311 + if (IS_ERR(vi)) 312 + goto put_dir_vi; 313 + 314 + xr = ntfs_index_ctx_get(NTFS_I(vi), reparse_index_name, 2); 315 + if (!xr) 316 + iput(vi); 317 + put_dir_vi: 318 + iput(dir_vi); 319 + return xr; 320 + } 321 + 322 + 323 + /* 324 + * Update the reparse data and index 325 + * 326 + * The reparse data attribute should have been created, and 327 + * an existing index is expected if there is an existing value. 328 + * 329 + */ 330 + static int update_reparse_data(struct ntfs_inode *ni, struct ntfs_index_context *xr, 331 + char *value, size_t size) 332 + { 333 + struct inode *rp_inode; 334 + int err = 0; 335 + s64 written; 336 + int oldsize; 337 + __le32 reparse_tag; 338 + struct ntfs_inode *rp_ni; 339 + 340 + rp_inode = ntfs_attr_iget(VFS_I(ni), AT_REPARSE_POINT, AT_UNNAMED, 0); 341 + if (IS_ERR(rp_inode)) 342 + return -EINVAL; 343 + rp_ni = NTFS_I(rp_inode); 344 + 345 + /* remove the existing reparse data */ 346 + oldsize = remove_reparse_index(rp_inode, xr, &reparse_tag); 347 + if (oldsize < 0) { 348 + err = oldsize; 349 + goto put_rp_inode; 350 + } 351 + 352 + /* overwrite value if any */ 353 + written = ntfs_inode_attr_pwrite(rp_inode, 0, size, value, false); 354 + if (written != size) { 355 + ntfs_error(ni->vol->sb, "Failed to update reparse data\n"); 356 + err = -EIO; 357 + goto put_rp_inode; 358 + } 359 + 360 + if (set_reparse_index(ni, xr, ((const struct reparse_point *)value)->reparse_tag) && 361 + oldsize > 0) { 362 + /* 363 + * If cannot index, try to remove the reparse 364 + * data and log the error. There will be an 365 + * inconsistency if removal fails. 366 + */ 367 + ntfs_attr_rm(rp_ni); 368 + ntfs_error(ni->vol->sb, 369 + "Failed to index reparse data. Possible corruption.\n"); 370 + } 371 + 372 + mark_mft_record_dirty(ni); 373 + put_rp_inode: 374 + iput(rp_inode); 375 + 376 + return err; 377 + } 378 + 379 + /* 380 + * Delete a reparse index entry 381 + */ 382 + int ntfs_delete_reparse_index(struct ntfs_inode *ni) 383 + { 384 + struct inode *vi; 385 + struct ntfs_index_context *xr; 386 + struct ntfs_inode *xrni; 387 + __le32 reparse_tag; 388 + int err = 0; 389 + 390 + if (!(ni->flags & FILE_ATTR_REPARSE_POINT)) 391 + return 0; 392 + 393 + vi = ntfs_attr_iget(VFS_I(ni), AT_REPARSE_POINT, AT_UNNAMED, 0); 394 + if (IS_ERR(vi)) 395 + return PTR_ERR(vi); 396 + 397 + /* 398 + * read the existing reparse data (the tag is enough) 399 + * and un-index it 400 + */ 401 + xr = open_reparse_index(ni->vol); 402 + if (xr) { 403 + xrni = xr->idx_ni; 404 + mutex_lock_nested(&xrni->mrec_lock, NTFS_EXTEND_MUTEX_PARENT); 405 + err = remove_reparse_index(vi, xr, &reparse_tag); 406 + if (err < 0) { 407 + ntfs_index_ctx_put(xr); 408 + mutex_unlock(&xrni->mrec_lock); 409 + iput(VFS_I(xrni)); 410 + goto out; 411 + } 412 + mark_mft_record_dirty(xrni); 413 + ntfs_index_ctx_put(xr); 414 + mutex_unlock(&xrni->mrec_lock); 415 + iput(VFS_I(xrni)); 416 + } 417 + 418 + ni->flags &= ~FILE_ATTR_REPARSE_POINT; 419 + NInoSetFileNameDirty(ni); 420 + mark_mft_record_dirty(ni); 421 + 422 + out: 423 + iput(vi); 424 + return err; 425 + } 426 + 427 + /* 428 + * Set the reparse data from an extended attribute 429 + */ 430 + static int ntfs_set_ntfs_reparse_data(struct ntfs_inode *ni, char *value, size_t size) 431 + { 432 + int err = 0; 433 + struct ntfs_inode *xrni; 434 + struct ntfs_index_context *xr; 435 + 436 + if (!ni) 437 + return -EINVAL; 438 + 439 + /* 440 + * reparse data compatibily with EA is not checked 441 + * any more, it is required by Windows 10, but may 442 + * lead to problems with earlier versions. 443 + */ 444 + if (valid_reparse_data(ni, (const struct reparse_point *)value, size) == false) 445 + return -EINVAL; 446 + 447 + xr = open_reparse_index(ni->vol); 448 + if (!xr) 449 + return -EINVAL; 450 + xrni = xr->idx_ni; 451 + 452 + if (!ntfs_attr_exist(ni, AT_REPARSE_POINT, AT_UNNAMED, 0)) { 453 + u8 dummy = 0; 454 + 455 + /* 456 + * no reparse data attribute : add one, 457 + * apparently, this does not feed the new value in 458 + * Note : NTFS version must be >= 3 459 + */ 460 + if (ni->vol->major_ver < 3) { 461 + err = -EOPNOTSUPP; 462 + ntfs_index_ctx_put(xr); 463 + goto out; 464 + } 465 + 466 + err = ntfs_attr_add(ni, AT_REPARSE_POINT, AT_UNNAMED, 0, &dummy, 0); 467 + if (err) { 468 + ntfs_index_ctx_put(xr); 469 + goto out; 470 + } 471 + ni->flags |= FILE_ATTR_REPARSE_POINT; 472 + NInoSetFileNameDirty(ni); 473 + mark_mft_record_dirty(ni); 474 + } 475 + 476 + /* update value and index */ 477 + mutex_lock_nested(&xrni->mrec_lock, NTFS_EXTEND_MUTEX_PARENT); 478 + err = update_reparse_data(ni, xr, value, size); 479 + if (err) { 480 + ni->flags &= ~FILE_ATTR_REPARSE_POINT; 481 + NInoSetFileNameDirty(ni); 482 + mark_mft_record_dirty(ni); 483 + } 484 + ntfs_index_ctx_put(xr); 485 + mutex_unlock(&xrni->mrec_lock); 486 + 487 + out: 488 + if (!err) 489 + mark_mft_record_dirty(xrni); 490 + iput(VFS_I(xrni)); 491 + 492 + return err; 493 + } 494 + 495 + /* 496 + * Set reparse data for a WSL type symlink 497 + */ 498 + int ntfs_reparse_set_wsl_symlink(struct ntfs_inode *ni, 499 + const __le16 *target, int target_len) 500 + { 501 + int err = 0; 502 + int len; 503 + int reparse_len; 504 + unsigned char *utarget = NULL; 505 + struct reparse_point *reparse; 506 + struct wsl_link_reparse_data *data; 507 + 508 + utarget = (char *)NULL; 509 + len = ntfs_ucstonls(ni->vol, target, target_len, &utarget, 0); 510 + if (len <= 0) 511 + return -EINVAL; 512 + 513 + reparse_len = sizeof(struct reparse_point) + sizeof(data->type) + len; 514 + reparse = kvzalloc(reparse_len, GFP_NOFS); 515 + if (!reparse) { 516 + err = -ENOMEM; 517 + kvfree(utarget); 518 + } else { 519 + data = (struct wsl_link_reparse_data *)reparse->reparse_data; 520 + reparse->reparse_tag = IO_REPARSE_TAG_LX_SYMLINK; 521 + reparse->reparse_data_length = 522 + cpu_to_le16(sizeof(data->type) + len); 523 + reparse->reserved = 0; 524 + data->type = cpu_to_le32(2); 525 + memcpy(data->link, utarget, len); 526 + err = ntfs_set_ntfs_reparse_data(ni, 527 + (char *)reparse, reparse_len); 528 + kvfree(reparse); 529 + if (!err) 530 + ni->target = utarget; 531 + } 532 + return err; 533 + } 534 + 535 + /* 536 + * Set reparse data for a WSL special file other than a symlink 537 + * (socket, fifo, character or block device) 538 + */ 539 + int ntfs_reparse_set_wsl_not_symlink(struct ntfs_inode *ni, mode_t mode) 540 + { 541 + int err; 542 + int len; 543 + int reparse_len; 544 + __le32 reparse_tag; 545 + struct reparse_point *reparse; 546 + 547 + len = 0; 548 + if (S_ISSOCK(mode)) 549 + reparse_tag = IO_REPARSE_TAG_AF_UNIX; 550 + else if (S_ISFIFO(mode)) 551 + reparse_tag = IO_REPARSE_TAG_LX_FIFO; 552 + else if (S_ISCHR(mode)) 553 + reparse_tag = IO_REPARSE_TAG_LX_CHR; 554 + else if (S_ISBLK(mode)) 555 + reparse_tag = IO_REPARSE_TAG_LX_BLK; 556 + else 557 + return -EOPNOTSUPP; 558 + 559 + reparse_len = sizeof(struct reparse_point) + len; 560 + reparse = kvzalloc(reparse_len, GFP_NOFS); 561 + if (!reparse) 562 + err = -ENOMEM; 563 + else { 564 + reparse->reparse_tag = reparse_tag; 565 + reparse->reparse_data_length = cpu_to_le16(len); 566 + reparse->reserved = cpu_to_le16(0); 567 + err = ntfs_set_ntfs_reparse_data(ni, (char *)reparse, 568 + reparse_len); 569 + kvfree(reparse); 570 + } 571 + 572 + return err; 573 + }