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 struct reparse_point rp = {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, (u8 *)&rp, sizeof(rp)); 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 } 574