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