xref: /linux/fs/ntfs/reparse.c (revision cdd4dc3aebeab43a72ce0bc2b5bab6f0a80b97a5)
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