1fc053f05SNamjae Jeon // SPDX-License-Identifier: GPL-2.0-or-later 2fc053f05SNamjae Jeon /* 3fc053f05SNamjae Jeon * Processing of reparse points 4fc053f05SNamjae Jeon * 5fc053f05SNamjae Jeon * Part of this file is based on code from the NTFS-3G. 6fc053f05SNamjae Jeon * 7fc053f05SNamjae Jeon * Copyright (c) 2008-2021 Jean-Pierre Andre 8fc053f05SNamjae Jeon * Copyright (c) 2025 LG Electronics Co., Ltd. 9fc053f05SNamjae Jeon */ 10fc053f05SNamjae Jeon 11fc053f05SNamjae Jeon #include "ntfs.h" 12fc053f05SNamjae Jeon #include "layout.h" 13fc053f05SNamjae Jeon #include "attrib.h" 14fc053f05SNamjae Jeon #include "inode.h" 15fc053f05SNamjae Jeon #include "dir.h" 16fc053f05SNamjae Jeon #include "volume.h" 17fc053f05SNamjae Jeon #include "mft.h" 18fc053f05SNamjae Jeon #include "index.h" 19fc053f05SNamjae Jeon #include "lcnalloc.h" 20fc053f05SNamjae Jeon #include "reparse.h" 21fc053f05SNamjae Jeon 22fc053f05SNamjae Jeon struct wsl_link_reparse_data { 23fc053f05SNamjae Jeon __le32 type; 24fc053f05SNamjae Jeon char link[]; 25fc053f05SNamjae Jeon }; 26fc053f05SNamjae Jeon 27fc053f05SNamjae Jeon /* Index entry in $Extend/$Reparse */ 28fc053f05SNamjae Jeon struct reparse_index { 29fc053f05SNamjae Jeon struct index_entry_header header; 30fc053f05SNamjae Jeon struct reparse_index_key key; 31fc053f05SNamjae Jeon __le32 filling; 32fc053f05SNamjae Jeon }; 33fc053f05SNamjae Jeon 34fc053f05SNamjae Jeon __le16 reparse_index_name[] = {cpu_to_le16('$'), cpu_to_le16('R'), 0}; 35fc053f05SNamjae Jeon 36fc053f05SNamjae Jeon 37fc053f05SNamjae Jeon /* 38fc053f05SNamjae Jeon * Check if the reparse point attribute buffer is valid. 39fc053f05SNamjae Jeon * Returns true if valid, false otherwise. 40fc053f05SNamjae Jeon */ 41fc053f05SNamjae Jeon static bool ntfs_is_valid_reparse_buffer(struct ntfs_inode *ni, 42fc053f05SNamjae Jeon const struct reparse_point *reparse_attr, size_t size) 43fc053f05SNamjae Jeon { 44fc053f05SNamjae Jeon size_t expected; 45fc053f05SNamjae Jeon 46fc053f05SNamjae Jeon if (!ni || !reparse_attr) 47fc053f05SNamjae Jeon return false; 48fc053f05SNamjae Jeon 49fc053f05SNamjae Jeon /* Minimum size must cover reparse_point header */ 50fc053f05SNamjae Jeon if (size < sizeof(struct reparse_point)) 51fc053f05SNamjae Jeon return false; 52fc053f05SNamjae Jeon 53fc053f05SNamjae Jeon /* Reserved zero tag is invalid */ 54fc053f05SNamjae Jeon if (reparse_attr->reparse_tag == IO_REPARSE_TAG_RESERVED_ZERO) 55fc053f05SNamjae Jeon return false; 56fc053f05SNamjae Jeon 57fc053f05SNamjae Jeon /* Calculate expected total size */ 58fc053f05SNamjae Jeon expected = sizeof(struct reparse_point) + 59fc053f05SNamjae Jeon le16_to_cpu(reparse_attr->reparse_data_length); 60fc053f05SNamjae Jeon 61fc053f05SNamjae Jeon /* Add GUID size for non-Microsoft tags */ 62fc053f05SNamjae Jeon if (!(reparse_attr->reparse_tag & IO_REPARSE_TAG_IS_MICROSOFT)) 63fc053f05SNamjae Jeon expected += sizeof(struct guid); 64fc053f05SNamjae Jeon 65fc053f05SNamjae Jeon /* Buffer must exactly match the expected size */ 66fc053f05SNamjae Jeon return expected == size; 67fc053f05SNamjae Jeon } 68fc053f05SNamjae Jeon 69fc053f05SNamjae Jeon /* 70fc053f05SNamjae Jeon * Do some sanity checks on reparse data 71fc053f05SNamjae Jeon * 72fc053f05SNamjae Jeon * Microsoft reparse points have an 8-byte header whereas 73fc053f05SNamjae Jeon * non-Microsoft reparse points have a 24-byte header. In each case, 74fc053f05SNamjae Jeon * 'reparse_data_length' must equal the number of non-header bytes. 75fc053f05SNamjae Jeon * 76fc053f05SNamjae Jeon * If the reparse data looks like a junction point or symbolic 77fc053f05SNamjae Jeon * link, more checks can be done. 78fc053f05SNamjae Jeon */ 79fc053f05SNamjae Jeon static bool valid_reparse_data(struct ntfs_inode *ni, 80fc053f05SNamjae Jeon const struct reparse_point *reparse_attr, size_t size) 81fc053f05SNamjae Jeon { 82fc053f05SNamjae Jeon const struct wsl_link_reparse_data *wsl_reparse_data = 83fc053f05SNamjae Jeon (const struct wsl_link_reparse_data *)reparse_attr->reparse_data; 84fc053f05SNamjae Jeon unsigned int data_len = le16_to_cpu(reparse_attr->reparse_data_length); 85fc053f05SNamjae Jeon 86fc053f05SNamjae Jeon if (ntfs_is_valid_reparse_buffer(ni, reparse_attr, size) == false) 87fc053f05SNamjae Jeon return false; 88fc053f05SNamjae Jeon 89fc053f05SNamjae Jeon switch (reparse_attr->reparse_tag) { 90fc053f05SNamjae Jeon case IO_REPARSE_TAG_LX_SYMLINK: 91fc053f05SNamjae Jeon if (data_len <= sizeof(wsl_reparse_data->type) || 92fc053f05SNamjae Jeon wsl_reparse_data->type != cpu_to_le32(2)) 93fc053f05SNamjae Jeon return false; 94fc053f05SNamjae Jeon break; 95fc053f05SNamjae Jeon case IO_REPARSE_TAG_AF_UNIX: 96fc053f05SNamjae Jeon case IO_REPARSE_TAG_LX_FIFO: 97fc053f05SNamjae Jeon case IO_REPARSE_TAG_LX_CHR: 98fc053f05SNamjae Jeon case IO_REPARSE_TAG_LX_BLK: 99fc053f05SNamjae Jeon if (data_len || !(ni->flags & FILE_ATTRIBUTE_RECALL_ON_OPEN)) 100fc053f05SNamjae Jeon return false; 101fc053f05SNamjae Jeon } 102fc053f05SNamjae Jeon 103fc053f05SNamjae Jeon return true; 104fc053f05SNamjae Jeon } 105fc053f05SNamjae Jeon 106fc053f05SNamjae Jeon static unsigned int ntfs_reparse_tag_mode(struct reparse_point *reparse_attr) 107fc053f05SNamjae Jeon { 108fc053f05SNamjae Jeon unsigned int mode = 0; 109fc053f05SNamjae Jeon 110fc053f05SNamjae Jeon switch (reparse_attr->reparse_tag) { 111fc053f05SNamjae Jeon case IO_REPARSE_TAG_SYMLINK: 112fc053f05SNamjae Jeon case IO_REPARSE_TAG_LX_SYMLINK: 113fc053f05SNamjae Jeon mode = S_IFLNK; 114fc053f05SNamjae Jeon break; 115fc053f05SNamjae Jeon case IO_REPARSE_TAG_AF_UNIX: 116fc053f05SNamjae Jeon mode = S_IFSOCK; 117fc053f05SNamjae Jeon break; 118fc053f05SNamjae Jeon case IO_REPARSE_TAG_LX_FIFO: 119fc053f05SNamjae Jeon mode = S_IFIFO; 120fc053f05SNamjae Jeon break; 121fc053f05SNamjae Jeon case IO_REPARSE_TAG_LX_CHR: 122fc053f05SNamjae Jeon mode = S_IFCHR; 123fc053f05SNamjae Jeon break; 124fc053f05SNamjae Jeon case IO_REPARSE_TAG_LX_BLK: 125fc053f05SNamjae Jeon mode = S_IFBLK; 126fc053f05SNamjae Jeon } 127fc053f05SNamjae Jeon 128fc053f05SNamjae Jeon return mode; 129fc053f05SNamjae Jeon } 130fc053f05SNamjae Jeon 131fc053f05SNamjae Jeon /* 132fc053f05SNamjae Jeon * Get the target for symbolic link 133fc053f05SNamjae Jeon */ 134fc053f05SNamjae Jeon unsigned int ntfs_make_symlink(struct ntfs_inode *ni) 135fc053f05SNamjae Jeon { 136fc053f05SNamjae Jeon s64 attr_size = 0; 137fc053f05SNamjae Jeon unsigned int lth; 138fc053f05SNamjae Jeon struct reparse_point *reparse_attr; 139fc053f05SNamjae Jeon struct wsl_link_reparse_data *wsl_link_data; 140fc053f05SNamjae Jeon unsigned int mode = 0; 141fc053f05SNamjae Jeon 142fc053f05SNamjae Jeon reparse_attr = ntfs_attr_readall(ni, AT_REPARSE_POINT, NULL, 0, 143fc053f05SNamjae Jeon &attr_size); 144fc053f05SNamjae Jeon if (reparse_attr && attr_size && 145fc053f05SNamjae Jeon valid_reparse_data(ni, reparse_attr, attr_size)) { 146fc053f05SNamjae Jeon switch (reparse_attr->reparse_tag) { 147fc053f05SNamjae Jeon case IO_REPARSE_TAG_LX_SYMLINK: 148fc053f05SNamjae Jeon wsl_link_data = 149fc053f05SNamjae Jeon (struct wsl_link_reparse_data *)reparse_attr->reparse_data; 150fc053f05SNamjae Jeon if (wsl_link_data->type == cpu_to_le32(2)) { 151fc053f05SNamjae Jeon lth = le16_to_cpu(reparse_attr->reparse_data_length) - 152fc053f05SNamjae Jeon sizeof(wsl_link_data->type); 153fc053f05SNamjae Jeon ni->target = kvzalloc(lth + 1, GFP_NOFS); 154fc053f05SNamjae Jeon if (ni->target) { 155fc053f05SNamjae Jeon memcpy(ni->target, wsl_link_data->link, lth); 156fc053f05SNamjae Jeon ni->target[lth] = 0; 157fc053f05SNamjae Jeon mode = ntfs_reparse_tag_mode(reparse_attr); 158fc053f05SNamjae Jeon } 159fc053f05SNamjae Jeon } 160fc053f05SNamjae Jeon break; 161fc053f05SNamjae Jeon default: 162fc053f05SNamjae Jeon mode = ntfs_reparse_tag_mode(reparse_attr); 163fc053f05SNamjae Jeon } 164fc053f05SNamjae Jeon } else 165fc053f05SNamjae Jeon ni->flags &= ~FILE_ATTR_REPARSE_POINT; 166fc053f05SNamjae Jeon 167fc053f05SNamjae Jeon if (reparse_attr) 168fc053f05SNamjae Jeon kvfree(reparse_attr); 169fc053f05SNamjae Jeon 170fc053f05SNamjae Jeon return mode; 171fc053f05SNamjae Jeon } 172fc053f05SNamjae Jeon 173fc053f05SNamjae Jeon unsigned int ntfs_reparse_tag_dt_types(struct ntfs_volume *vol, unsigned long mref) 174fc053f05SNamjae Jeon { 175fc053f05SNamjae Jeon s64 attr_size = 0; 176fc053f05SNamjae Jeon struct reparse_point *reparse_attr; 177fc053f05SNamjae Jeon unsigned int dt_type = DT_UNKNOWN; 178fc053f05SNamjae Jeon struct inode *vi; 179fc053f05SNamjae Jeon 180fc053f05SNamjae Jeon vi = ntfs_iget(vol->sb, mref); 181fc053f05SNamjae Jeon if (IS_ERR(vi)) 182fc053f05SNamjae Jeon return PTR_ERR(vi); 183fc053f05SNamjae Jeon 184fc053f05SNamjae Jeon reparse_attr = (struct reparse_point *)ntfs_attr_readall(NTFS_I(vi), 185fc053f05SNamjae Jeon AT_REPARSE_POINT, NULL, 0, &attr_size); 186fc053f05SNamjae Jeon 187fc053f05SNamjae Jeon if (reparse_attr && attr_size) { 188fc053f05SNamjae Jeon switch (reparse_attr->reparse_tag) { 189fc053f05SNamjae Jeon case IO_REPARSE_TAG_SYMLINK: 190fc053f05SNamjae Jeon case IO_REPARSE_TAG_LX_SYMLINK: 191fc053f05SNamjae Jeon dt_type = DT_LNK; 192fc053f05SNamjae Jeon break; 193fc053f05SNamjae Jeon case IO_REPARSE_TAG_AF_UNIX: 194fc053f05SNamjae Jeon dt_type = DT_SOCK; 195fc053f05SNamjae Jeon break; 196fc053f05SNamjae Jeon case IO_REPARSE_TAG_LX_FIFO: 197fc053f05SNamjae Jeon dt_type = DT_FIFO; 198fc053f05SNamjae Jeon break; 199fc053f05SNamjae Jeon case IO_REPARSE_TAG_LX_CHR: 200fc053f05SNamjae Jeon dt_type = DT_CHR; 201fc053f05SNamjae Jeon break; 202fc053f05SNamjae Jeon case IO_REPARSE_TAG_LX_BLK: 203fc053f05SNamjae Jeon dt_type = DT_BLK; 204fc053f05SNamjae Jeon } 205fc053f05SNamjae Jeon } 206fc053f05SNamjae Jeon 207fc053f05SNamjae Jeon if (reparse_attr) 208fc053f05SNamjae Jeon kvfree(reparse_attr); 209fc053f05SNamjae Jeon 210fc053f05SNamjae Jeon iput(vi); 211fc053f05SNamjae Jeon return dt_type; 212fc053f05SNamjae Jeon } 213fc053f05SNamjae Jeon 214fc053f05SNamjae Jeon /* 215fc053f05SNamjae Jeon * Set the index for new reparse data 216fc053f05SNamjae Jeon */ 217fc053f05SNamjae Jeon static int set_reparse_index(struct ntfs_inode *ni, struct ntfs_index_context *xr, 218fc053f05SNamjae Jeon __le32 reparse_tag) 219fc053f05SNamjae Jeon { 220fc053f05SNamjae Jeon struct reparse_index indx; 221fc053f05SNamjae Jeon u64 file_id_cpu; 222fc053f05SNamjae Jeon __le64 file_id; 223fc053f05SNamjae Jeon 224fc053f05SNamjae Jeon file_id_cpu = MK_MREF(ni->mft_no, ni->seq_no); 225fc053f05SNamjae Jeon file_id = cpu_to_le64(file_id_cpu); 226fc053f05SNamjae Jeon indx.header.data.vi.data_offset = 227fc053f05SNamjae Jeon cpu_to_le16(sizeof(struct index_entry_header) + sizeof(struct reparse_index_key)); 228fc053f05SNamjae Jeon indx.header.data.vi.data_length = 0; 229fc053f05SNamjae Jeon indx.header.data.vi.reservedV = 0; 230fc053f05SNamjae Jeon indx.header.length = cpu_to_le16(sizeof(struct reparse_index)); 231fc053f05SNamjae Jeon indx.header.key_length = cpu_to_le16(sizeof(struct reparse_index_key)); 232fc053f05SNamjae Jeon indx.header.flags = 0; 233fc053f05SNamjae Jeon indx.header.reserved = 0; 234fc053f05SNamjae Jeon indx.key.reparse_tag = reparse_tag; 235fc053f05SNamjae Jeon /* danger on processors which require proper alignment! */ 236fc053f05SNamjae Jeon memcpy(&indx.key.file_id, &file_id, 8); 237fc053f05SNamjae Jeon indx.filling = 0; 238fc053f05SNamjae Jeon ntfs_index_ctx_reinit(xr); 239fc053f05SNamjae Jeon 240fc053f05SNamjae Jeon return ntfs_ie_add(xr, (struct index_entry *)&indx); 241fc053f05SNamjae Jeon } 242fc053f05SNamjae Jeon 243fc053f05SNamjae Jeon /* 244fc053f05SNamjae Jeon * Remove a reparse data index entry if attribute present 245fc053f05SNamjae Jeon */ 246fc053f05SNamjae Jeon static int remove_reparse_index(struct inode *rp, struct ntfs_index_context *xr, 247fc053f05SNamjae Jeon __le32 *preparse_tag) 248fc053f05SNamjae Jeon { 249fc053f05SNamjae Jeon struct reparse_index_key key; 250fc053f05SNamjae Jeon u64 file_id_cpu; 251fc053f05SNamjae Jeon __le64 file_id; 252fc053f05SNamjae Jeon s64 size; 253fc053f05SNamjae Jeon struct ntfs_inode *ni = NTFS_I(rp); 254fc053f05SNamjae Jeon int err = 0, ret = ni->data_size; 255fc053f05SNamjae Jeon 256fc053f05SNamjae Jeon if (ni->data_size == 0) 257fc053f05SNamjae Jeon return 0; 258fc053f05SNamjae Jeon 259fc053f05SNamjae Jeon /* read the existing reparse_tag */ 260fc053f05SNamjae Jeon size = ntfs_inode_attr_pread(rp, 0, 4, (char *)preparse_tag); 261fc053f05SNamjae Jeon if (size != 4) 262fc053f05SNamjae Jeon return -ENODATA; 263fc053f05SNamjae Jeon 264fc053f05SNamjae Jeon file_id_cpu = MK_MREF(ni->mft_no, ni->seq_no); 265fc053f05SNamjae Jeon file_id = cpu_to_le64(file_id_cpu); 266fc053f05SNamjae Jeon key.reparse_tag = *preparse_tag; 267fc053f05SNamjae Jeon /* danger on processors which require proper alignment! */ 268fc053f05SNamjae Jeon memcpy(&key.file_id, &file_id, 8); 269fc053f05SNamjae Jeon if (!ntfs_index_lookup(&key, sizeof(struct reparse_index_key), xr)) { 270fc053f05SNamjae Jeon err = ntfs_index_rm(xr); 271fc053f05SNamjae Jeon if (err) 272fc053f05SNamjae Jeon ret = err; 273fc053f05SNamjae Jeon } 274fc053f05SNamjae Jeon return ret; 275fc053f05SNamjae Jeon } 276fc053f05SNamjae Jeon 277fc053f05SNamjae Jeon /* 278fc053f05SNamjae Jeon * Open the $Extend/$Reparse file and its index 279fc053f05SNamjae Jeon */ 280fc053f05SNamjae Jeon static struct ntfs_index_context *open_reparse_index(struct ntfs_volume *vol) 281fc053f05SNamjae Jeon { 282fc053f05SNamjae Jeon struct ntfs_index_context *xr = NULL; 283fc053f05SNamjae Jeon u64 mref; 284fc053f05SNamjae Jeon __le16 *uname; 285fc053f05SNamjae Jeon struct ntfs_name *name = NULL; 286fc053f05SNamjae Jeon int uname_len; 287fc053f05SNamjae Jeon struct inode *vi, *dir_vi; 288fc053f05SNamjae Jeon 289fc053f05SNamjae Jeon /* do not use path_name_to inode - could reopen root */ 290fc053f05SNamjae Jeon dir_vi = ntfs_iget(vol->sb, FILE_Extend); 291fc053f05SNamjae Jeon if (IS_ERR(dir_vi)) 292fc053f05SNamjae Jeon return NULL; 293fc053f05SNamjae Jeon 294fc053f05SNamjae Jeon uname_len = ntfs_nlstoucs(vol, "$Reparse", 8, &uname, 295fc053f05SNamjae Jeon NTFS_MAX_NAME_LEN); 296fc053f05SNamjae Jeon if (uname_len < 0) { 297fc053f05SNamjae Jeon iput(dir_vi); 298fc053f05SNamjae Jeon return NULL; 299fc053f05SNamjae Jeon } 300fc053f05SNamjae Jeon 301fc053f05SNamjae Jeon mutex_lock_nested(&NTFS_I(dir_vi)->mrec_lock, NTFS_EXTEND_MUTEX_PARENT); 302fc053f05SNamjae Jeon mref = ntfs_lookup_inode_by_name(NTFS_I(dir_vi), uname, uname_len, 303fc053f05SNamjae Jeon &name); 304fc053f05SNamjae Jeon mutex_unlock(&NTFS_I(dir_vi)->mrec_lock); 305fc053f05SNamjae Jeon kfree(name); 306fc053f05SNamjae Jeon kmem_cache_free(ntfs_name_cache, uname); 307fc053f05SNamjae Jeon if (IS_ERR_MREF(mref)) 308fc053f05SNamjae Jeon goto put_dir_vi; 309fc053f05SNamjae Jeon 310fc053f05SNamjae Jeon vi = ntfs_iget(vol->sb, MREF(mref)); 311fc053f05SNamjae Jeon if (IS_ERR(vi)) 312fc053f05SNamjae Jeon goto put_dir_vi; 313fc053f05SNamjae Jeon 314fc053f05SNamjae Jeon xr = ntfs_index_ctx_get(NTFS_I(vi), reparse_index_name, 2); 315fc053f05SNamjae Jeon if (!xr) 316fc053f05SNamjae Jeon iput(vi); 317fc053f05SNamjae Jeon put_dir_vi: 318fc053f05SNamjae Jeon iput(dir_vi); 319fc053f05SNamjae Jeon return xr; 320fc053f05SNamjae Jeon } 321fc053f05SNamjae Jeon 322fc053f05SNamjae Jeon 323fc053f05SNamjae Jeon /* 324fc053f05SNamjae Jeon * Update the reparse data and index 325fc053f05SNamjae Jeon * 326fc053f05SNamjae Jeon * The reparse data attribute should have been created, and 327fc053f05SNamjae Jeon * an existing index is expected if there is an existing value. 328fc053f05SNamjae Jeon * 329fc053f05SNamjae Jeon */ 330fc053f05SNamjae Jeon static int update_reparse_data(struct ntfs_inode *ni, struct ntfs_index_context *xr, 331fc053f05SNamjae Jeon char *value, size_t size) 332fc053f05SNamjae Jeon { 333fc053f05SNamjae Jeon struct inode *rp_inode; 334fc053f05SNamjae Jeon int err = 0; 335fc053f05SNamjae Jeon s64 written; 336fc053f05SNamjae Jeon int oldsize; 337fc053f05SNamjae Jeon __le32 reparse_tag; 338fc053f05SNamjae Jeon struct ntfs_inode *rp_ni; 339fc053f05SNamjae Jeon 340fc053f05SNamjae Jeon rp_inode = ntfs_attr_iget(VFS_I(ni), AT_REPARSE_POINT, AT_UNNAMED, 0); 341fc053f05SNamjae Jeon if (IS_ERR(rp_inode)) 342fc053f05SNamjae Jeon return -EINVAL; 343fc053f05SNamjae Jeon rp_ni = NTFS_I(rp_inode); 344fc053f05SNamjae Jeon 345fc053f05SNamjae Jeon /* remove the existing reparse data */ 346fc053f05SNamjae Jeon oldsize = remove_reparse_index(rp_inode, xr, &reparse_tag); 347fc053f05SNamjae Jeon if (oldsize < 0) { 348fc053f05SNamjae Jeon err = oldsize; 349fc053f05SNamjae Jeon goto put_rp_inode; 350fc053f05SNamjae Jeon } 351fc053f05SNamjae Jeon 352fc053f05SNamjae Jeon /* overwrite value if any */ 353fc053f05SNamjae Jeon written = ntfs_inode_attr_pwrite(rp_inode, 0, size, value, false); 354fc053f05SNamjae Jeon if (written != size) { 355fc053f05SNamjae Jeon ntfs_error(ni->vol->sb, "Failed to update reparse data\n"); 356fc053f05SNamjae Jeon err = -EIO; 357fc053f05SNamjae Jeon goto put_rp_inode; 358fc053f05SNamjae Jeon } 359fc053f05SNamjae Jeon 360fc053f05SNamjae Jeon if (set_reparse_index(ni, xr, ((const struct reparse_point *)value)->reparse_tag) && 361fc053f05SNamjae Jeon oldsize > 0) { 362fc053f05SNamjae Jeon /* 363fc053f05SNamjae Jeon * If cannot index, try to remove the reparse 364fc053f05SNamjae Jeon * data and log the error. There will be an 365fc053f05SNamjae Jeon * inconsistency if removal fails. 366fc053f05SNamjae Jeon */ 367fc053f05SNamjae Jeon ntfs_attr_rm(rp_ni); 368fc053f05SNamjae Jeon ntfs_error(ni->vol->sb, 369fc053f05SNamjae Jeon "Failed to index reparse data. Possible corruption.\n"); 370fc053f05SNamjae Jeon } 371fc053f05SNamjae Jeon 372fc053f05SNamjae Jeon mark_mft_record_dirty(ni); 373fc053f05SNamjae Jeon put_rp_inode: 374fc053f05SNamjae Jeon iput(rp_inode); 375fc053f05SNamjae Jeon 376fc053f05SNamjae Jeon return err; 377fc053f05SNamjae Jeon } 378fc053f05SNamjae Jeon 379fc053f05SNamjae Jeon /* 380fc053f05SNamjae Jeon * Delete a reparse index entry 381fc053f05SNamjae Jeon */ 382fc053f05SNamjae Jeon int ntfs_delete_reparse_index(struct ntfs_inode *ni) 383fc053f05SNamjae Jeon { 384fc053f05SNamjae Jeon struct inode *vi; 385fc053f05SNamjae Jeon struct ntfs_index_context *xr; 386fc053f05SNamjae Jeon struct ntfs_inode *xrni; 387fc053f05SNamjae Jeon __le32 reparse_tag; 388fc053f05SNamjae Jeon int err = 0; 389fc053f05SNamjae Jeon 390fc053f05SNamjae Jeon if (!(ni->flags & FILE_ATTR_REPARSE_POINT)) 391fc053f05SNamjae Jeon return 0; 392fc053f05SNamjae Jeon 393fc053f05SNamjae Jeon vi = ntfs_attr_iget(VFS_I(ni), AT_REPARSE_POINT, AT_UNNAMED, 0); 394fc053f05SNamjae Jeon if (IS_ERR(vi)) 395fc053f05SNamjae Jeon return PTR_ERR(vi); 396fc053f05SNamjae Jeon 397fc053f05SNamjae Jeon /* 398fc053f05SNamjae Jeon * read the existing reparse data (the tag is enough) 399fc053f05SNamjae Jeon * and un-index it 400fc053f05SNamjae Jeon */ 401fc053f05SNamjae Jeon xr = open_reparse_index(ni->vol); 402fc053f05SNamjae Jeon if (xr) { 403fc053f05SNamjae Jeon xrni = xr->idx_ni; 404fc053f05SNamjae Jeon mutex_lock_nested(&xrni->mrec_lock, NTFS_EXTEND_MUTEX_PARENT); 405fc053f05SNamjae Jeon err = remove_reparse_index(vi, xr, &reparse_tag); 406fc053f05SNamjae Jeon if (err < 0) { 407fc053f05SNamjae Jeon ntfs_index_ctx_put(xr); 408fc053f05SNamjae Jeon mutex_unlock(&xrni->mrec_lock); 409fc053f05SNamjae Jeon iput(VFS_I(xrni)); 410fc053f05SNamjae Jeon goto out; 411fc053f05SNamjae Jeon } 412fc053f05SNamjae Jeon mark_mft_record_dirty(xrni); 413fc053f05SNamjae Jeon ntfs_index_ctx_put(xr); 414fc053f05SNamjae Jeon mutex_unlock(&xrni->mrec_lock); 415fc053f05SNamjae Jeon iput(VFS_I(xrni)); 416fc053f05SNamjae Jeon } 417fc053f05SNamjae Jeon 418fc053f05SNamjae Jeon ni->flags &= ~FILE_ATTR_REPARSE_POINT; 419fc053f05SNamjae Jeon NInoSetFileNameDirty(ni); 420fc053f05SNamjae Jeon mark_mft_record_dirty(ni); 421fc053f05SNamjae Jeon 422fc053f05SNamjae Jeon out: 423fc053f05SNamjae Jeon iput(vi); 424fc053f05SNamjae Jeon return err; 425fc053f05SNamjae Jeon } 426fc053f05SNamjae Jeon 427fc053f05SNamjae Jeon /* 428fc053f05SNamjae Jeon * Set the reparse data from an extended attribute 429fc053f05SNamjae Jeon */ 430fc053f05SNamjae Jeon static int ntfs_set_ntfs_reparse_data(struct ntfs_inode *ni, char *value, size_t size) 431fc053f05SNamjae Jeon { 432fc053f05SNamjae Jeon int err = 0; 433fc053f05SNamjae Jeon struct ntfs_inode *xrni; 434fc053f05SNamjae Jeon struct ntfs_index_context *xr; 435fc053f05SNamjae Jeon 436fc053f05SNamjae Jeon if (!ni) 437fc053f05SNamjae Jeon return -EINVAL; 438fc053f05SNamjae Jeon 439fc053f05SNamjae Jeon /* 440fc053f05SNamjae Jeon * reparse data compatibily with EA is not checked 441fc053f05SNamjae Jeon * any more, it is required by Windows 10, but may 442fc053f05SNamjae Jeon * lead to problems with earlier versions. 443fc053f05SNamjae Jeon */ 444fc053f05SNamjae Jeon if (valid_reparse_data(ni, (const struct reparse_point *)value, size) == false) 445fc053f05SNamjae Jeon return -EINVAL; 446fc053f05SNamjae Jeon 447fc053f05SNamjae Jeon xr = open_reparse_index(ni->vol); 448fc053f05SNamjae Jeon if (!xr) 449fc053f05SNamjae Jeon return -EINVAL; 450fc053f05SNamjae Jeon xrni = xr->idx_ni; 451fc053f05SNamjae Jeon 452fc053f05SNamjae Jeon if (!ntfs_attr_exist(ni, AT_REPARSE_POINT, AT_UNNAMED, 0)) { 453*6ceb4cc8SHyunchul Lee struct reparse_point rp = {0, }; 454fc053f05SNamjae Jeon 455fc053f05SNamjae Jeon /* 456fc053f05SNamjae Jeon * no reparse data attribute : add one, 457fc053f05SNamjae Jeon * apparently, this does not feed the new value in 458fc053f05SNamjae Jeon * Note : NTFS version must be >= 3 459fc053f05SNamjae Jeon */ 460fc053f05SNamjae Jeon if (ni->vol->major_ver < 3) { 461fc053f05SNamjae Jeon err = -EOPNOTSUPP; 462fc053f05SNamjae Jeon ntfs_index_ctx_put(xr); 463fc053f05SNamjae Jeon goto out; 464fc053f05SNamjae Jeon } 465fc053f05SNamjae Jeon 466*6ceb4cc8SHyunchul Lee err = ntfs_attr_add(ni, AT_REPARSE_POINT, AT_UNNAMED, 0, (u8 *)&rp, sizeof(rp)); 467fc053f05SNamjae Jeon if (err) { 468fc053f05SNamjae Jeon ntfs_index_ctx_put(xr); 469fc053f05SNamjae Jeon goto out; 470fc053f05SNamjae Jeon } 471fc053f05SNamjae Jeon ni->flags |= FILE_ATTR_REPARSE_POINT; 472fc053f05SNamjae Jeon NInoSetFileNameDirty(ni); 473fc053f05SNamjae Jeon mark_mft_record_dirty(ni); 474fc053f05SNamjae Jeon } 475fc053f05SNamjae Jeon 476fc053f05SNamjae Jeon /* update value and index */ 477fc053f05SNamjae Jeon mutex_lock_nested(&xrni->mrec_lock, NTFS_EXTEND_MUTEX_PARENT); 478fc053f05SNamjae Jeon err = update_reparse_data(ni, xr, value, size); 479fc053f05SNamjae Jeon if (err) { 480fc053f05SNamjae Jeon ni->flags &= ~FILE_ATTR_REPARSE_POINT; 481fc053f05SNamjae Jeon NInoSetFileNameDirty(ni); 482fc053f05SNamjae Jeon mark_mft_record_dirty(ni); 483fc053f05SNamjae Jeon } 484fc053f05SNamjae Jeon ntfs_index_ctx_put(xr); 485fc053f05SNamjae Jeon mutex_unlock(&xrni->mrec_lock); 486fc053f05SNamjae Jeon 487fc053f05SNamjae Jeon out: 488fc053f05SNamjae Jeon if (!err) 489fc053f05SNamjae Jeon mark_mft_record_dirty(xrni); 490fc053f05SNamjae Jeon iput(VFS_I(xrni)); 491fc053f05SNamjae Jeon 492fc053f05SNamjae Jeon return err; 493fc053f05SNamjae Jeon } 494fc053f05SNamjae Jeon 495fc053f05SNamjae Jeon /* 496fc053f05SNamjae Jeon * Set reparse data for a WSL type symlink 497fc053f05SNamjae Jeon */ 498fc053f05SNamjae Jeon int ntfs_reparse_set_wsl_symlink(struct ntfs_inode *ni, 499fc053f05SNamjae Jeon const __le16 *target, int target_len) 500fc053f05SNamjae Jeon { 501fc053f05SNamjae Jeon int err = 0; 502fc053f05SNamjae Jeon int len; 503fc053f05SNamjae Jeon int reparse_len; 504fc053f05SNamjae Jeon unsigned char *utarget = NULL; 505fc053f05SNamjae Jeon struct reparse_point *reparse; 506fc053f05SNamjae Jeon struct wsl_link_reparse_data *data; 507fc053f05SNamjae Jeon 508fc053f05SNamjae Jeon utarget = (char *)NULL; 509fc053f05SNamjae Jeon len = ntfs_ucstonls(ni->vol, target, target_len, &utarget, 0); 510fc053f05SNamjae Jeon if (len <= 0) 511fc053f05SNamjae Jeon return -EINVAL; 512fc053f05SNamjae Jeon 513fc053f05SNamjae Jeon reparse_len = sizeof(struct reparse_point) + sizeof(data->type) + len; 514fc053f05SNamjae Jeon reparse = kvzalloc(reparse_len, GFP_NOFS); 515fc053f05SNamjae Jeon if (!reparse) { 516fc053f05SNamjae Jeon err = -ENOMEM; 517fc053f05SNamjae Jeon kvfree(utarget); 518fc053f05SNamjae Jeon } else { 519fc053f05SNamjae Jeon data = (struct wsl_link_reparse_data *)reparse->reparse_data; 520fc053f05SNamjae Jeon reparse->reparse_tag = IO_REPARSE_TAG_LX_SYMLINK; 521fc053f05SNamjae Jeon reparse->reparse_data_length = 522fc053f05SNamjae Jeon cpu_to_le16(sizeof(data->type) + len); 523fc053f05SNamjae Jeon reparse->reserved = 0; 524fc053f05SNamjae Jeon data->type = cpu_to_le32(2); 525fc053f05SNamjae Jeon memcpy(data->link, utarget, len); 526fc053f05SNamjae Jeon err = ntfs_set_ntfs_reparse_data(ni, 527fc053f05SNamjae Jeon (char *)reparse, reparse_len); 528fc053f05SNamjae Jeon kvfree(reparse); 529fc053f05SNamjae Jeon if (!err) 530fc053f05SNamjae Jeon ni->target = utarget; 531fc053f05SNamjae Jeon } 532fc053f05SNamjae Jeon return err; 533fc053f05SNamjae Jeon } 534fc053f05SNamjae Jeon 535fc053f05SNamjae Jeon /* 536fc053f05SNamjae Jeon * Set reparse data for a WSL special file other than a symlink 537fc053f05SNamjae Jeon * (socket, fifo, character or block device) 538fc053f05SNamjae Jeon */ 539fc053f05SNamjae Jeon int ntfs_reparse_set_wsl_not_symlink(struct ntfs_inode *ni, mode_t mode) 540fc053f05SNamjae Jeon { 541fc053f05SNamjae Jeon int err; 542fc053f05SNamjae Jeon int len; 543fc053f05SNamjae Jeon int reparse_len; 544fc053f05SNamjae Jeon __le32 reparse_tag; 545fc053f05SNamjae Jeon struct reparse_point *reparse; 546fc053f05SNamjae Jeon 547fc053f05SNamjae Jeon len = 0; 548fc053f05SNamjae Jeon if (S_ISSOCK(mode)) 549fc053f05SNamjae Jeon reparse_tag = IO_REPARSE_TAG_AF_UNIX; 550fc053f05SNamjae Jeon else if (S_ISFIFO(mode)) 551fc053f05SNamjae Jeon reparse_tag = IO_REPARSE_TAG_LX_FIFO; 552fc053f05SNamjae Jeon else if (S_ISCHR(mode)) 553fc053f05SNamjae Jeon reparse_tag = IO_REPARSE_TAG_LX_CHR; 554fc053f05SNamjae Jeon else if (S_ISBLK(mode)) 555fc053f05SNamjae Jeon reparse_tag = IO_REPARSE_TAG_LX_BLK; 556fc053f05SNamjae Jeon else 557fc053f05SNamjae Jeon return -EOPNOTSUPP; 558fc053f05SNamjae Jeon 559fc053f05SNamjae Jeon reparse_len = sizeof(struct reparse_point) + len; 560fc053f05SNamjae Jeon reparse = kvzalloc(reparse_len, GFP_NOFS); 561fc053f05SNamjae Jeon if (!reparse) 562fc053f05SNamjae Jeon err = -ENOMEM; 563fc053f05SNamjae Jeon else { 564fc053f05SNamjae Jeon reparse->reparse_tag = reparse_tag; 565fc053f05SNamjae Jeon reparse->reparse_data_length = cpu_to_le16(len); 566fc053f05SNamjae Jeon reparse->reserved = cpu_to_le16(0); 567fc053f05SNamjae Jeon err = ntfs_set_ntfs_reparse_data(ni, (char *)reparse, 568fc053f05SNamjae Jeon reparse_len); 569fc053f05SNamjae Jeon kvfree(reparse); 570fc053f05SNamjae Jeon } 571fc053f05SNamjae Jeon 572fc053f05SNamjae Jeon return err; 573fc053f05SNamjae Jeon } 574