Commit | Line | Data |
---|---|---|
be71b5cb KK |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* | |
3 | * | |
4 | * Copyright (C) 2019-2021 Paragon Software GmbH, All rights reserved. | |
5 | * | |
6 | */ | |
7 | ||
be71b5cb | 8 | #include <linux/fs.h> |
be71b5cb KK |
9 | #include <linux/posix_acl.h> |
10 | #include <linux/posix_acl_xattr.h> | |
11 | #include <linux/xattr.h> | |
12 | ||
13 | #include "debug.h" | |
14 | #include "ntfs.h" | |
15 | #include "ntfs_fs.h" | |
16 | ||
17 | // clang-format off | |
0d19f3d7 DP |
18 | #define SYSTEM_DOS_ATTRIB "system.dos_attrib" |
19 | #define SYSTEM_NTFS_ATTRIB "system.ntfs_attrib" | |
20 | #define SYSTEM_NTFS_ATTRIB_BE "system.ntfs_attrib_be" | |
21 | #define SYSTEM_NTFS_SECURITY "system.ntfs_security" | |
be71b5cb KK |
22 | // clang-format on |
23 | ||
24 | static inline size_t unpacked_ea_size(const struct EA_FULL *ea) | |
25 | { | |
96de65a9 | 26 | return ea->size ? le32_to_cpu(ea->size) : |
f0377761 | 27 | ALIGN(struct_size(ea, name, |
d3624466 KK |
28 | 1 + ea->name_len + |
29 | le16_to_cpu(ea->elength)), | |
30 | 4); | |
be71b5cb KK |
31 | } |
32 | ||
33 | static inline size_t packed_ea_size(const struct EA_FULL *ea) | |
34 | { | |
35 | return struct_size(ea, name, | |
36 | 1 + ea->name_len + le16_to_cpu(ea->elength)) - | |
37 | offsetof(struct EA_FULL, flags); | |
38 | } | |
39 | ||
40 | /* | |
41 | * find_ea | |
42 | * | |
e8b8e97f | 43 | * Assume there is at least one xattr in the list. |
be71b5cb KK |
44 | */ |
45 | static inline bool find_ea(const struct EA_FULL *ea_all, u32 bytes, | |
0e8235d2 | 46 | const char *name, u8 name_len, u32 *off, u32 *ea_sz) |
be71b5cb | 47 | { |
0e8235d2 | 48 | u32 ea_size; |
be71b5cb | 49 | |
0e8235d2 KK |
50 | *off = 0; |
51 | if (!ea_all) | |
be71b5cb KK |
52 | return false; |
53 | ||
0e8235d2 | 54 | for (; *off < bytes; *off += ea_size) { |
be71b5cb | 55 | const struct EA_FULL *ea = Add2Ptr(ea_all, *off); |
0e8235d2 | 56 | ea_size = unpacked_ea_size(ea); |
be71b5cb | 57 | if (ea->name_len == name_len && |
0e8235d2 KK |
58 | !memcmp(ea->name, name, name_len)) { |
59 | if (ea_sz) | |
60 | *ea_sz = ea_size; | |
be71b5cb | 61 | return true; |
0e8235d2 | 62 | } |
be71b5cb | 63 | } |
0e8235d2 KK |
64 | |
65 | return false; | |
be71b5cb KK |
66 | } |
67 | ||
68 | /* | |
e8b8e97f KA |
69 | * ntfs_read_ea - Read all extended attributes. |
70 | * @ea: New allocated memory. | |
71 | * @info: Pointer into resident data. | |
be71b5cb KK |
72 | */ |
73 | static int ntfs_read_ea(struct ntfs_inode *ni, struct EA_FULL **ea, | |
74 | size_t add_bytes, const struct EA_INFO **info) | |
75 | { | |
0e8235d2 | 76 | int err = -EINVAL; |
cff32466 | 77 | struct ntfs_sb_info *sbi = ni->mi.sbi; |
be71b5cb KK |
78 | struct ATTR_LIST_ENTRY *le = NULL; |
79 | struct ATTRIB *attr_info, *attr_ea; | |
80 | void *ea_p; | |
0e8235d2 | 81 | u32 size, off, ea_size; |
be71b5cb KK |
82 | |
83 | static_assert(le32_to_cpu(ATTR_EA_INFO) < le32_to_cpu(ATTR_EA)); | |
84 | ||
85 | *ea = NULL; | |
86 | *info = NULL; | |
87 | ||
88 | attr_info = | |
89 | ni_find_attr(ni, NULL, &le, ATTR_EA_INFO, NULL, 0, NULL, NULL); | |
90 | attr_ea = | |
91 | ni_find_attr(ni, attr_info, &le, ATTR_EA, NULL, 0, NULL, NULL); | |
92 | ||
93 | if (!attr_ea || !attr_info) | |
94 | return 0; | |
95 | ||
96 | *info = resident_data_ex(attr_info, sizeof(struct EA_INFO)); | |
97 | if (!*info) | |
0e8235d2 | 98 | goto out; |
be71b5cb | 99 | |
e8b8e97f | 100 | /* Check Ea limit. */ |
be71b5cb | 101 | size = le32_to_cpu((*info)->size); |
0e8235d2 KK |
102 | if (size > sbi->ea_max_size) { |
103 | err = -EFBIG; | |
104 | goto out; | |
105 | } | |
be71b5cb | 106 | |
0e8235d2 KK |
107 | if (attr_size(attr_ea) > sbi->ea_max_size) { |
108 | err = -EFBIG; | |
109 | goto out; | |
110 | } | |
111 | ||
112 | if (!size) { | |
113 | /* EA info persists, but xattr is empty. Looks like EA problem. */ | |
114 | goto out; | |
115 | } | |
be71b5cb | 116 | |
e8b8e97f | 117 | /* Allocate memory for packed Ea. */ |
e001e608 | 118 | ea_p = kmalloc(size_add(size, add_bytes), GFP_NOFS); |
be71b5cb KK |
119 | if (!ea_p) |
120 | return -ENOMEM; | |
121 | ||
0e8235d2 | 122 | if (attr_ea->non_res) { |
be71b5cb KK |
123 | struct runs_tree run; |
124 | ||
125 | run_init(&run); | |
126 | ||
42f86b12 | 127 | err = attr_load_runs_range(ni, ATTR_EA, NULL, 0, &run, 0, size); |
be71b5cb | 128 | if (!err) |
cff32466 | 129 | err = ntfs_read_run_nb(sbi, &run, 0, ea_p, size, NULL); |
be71b5cb KK |
130 | run_close(&run); |
131 | ||
132 | if (err) | |
0e8235d2 | 133 | goto out1; |
be71b5cb KK |
134 | } else { |
135 | void *p = resident_data_ex(attr_ea, size); | |
136 | ||
0e8235d2 KK |
137 | if (!p) |
138 | goto out1; | |
be71b5cb KK |
139 | memcpy(ea_p, p, size); |
140 | } | |
141 | ||
142 | memset(Add2Ptr(ea_p, size), 0, add_bytes); | |
0e8235d2 | 143 | |
c9db0ff0 | 144 | err = -EINVAL; |
0e8235d2 KK |
145 | /* Check all attributes for consistency. */ |
146 | for (off = 0; off < size; off += ea_size) { | |
147 | const struct EA_FULL *ef = Add2Ptr(ea_p, off); | |
148 | u32 bytes = size - off; | |
149 | ||
150 | /* Check if we can use field ea->size. */ | |
151 | if (bytes < sizeof(ef->size)) | |
152 | goto out1; | |
153 | ||
154 | if (ef->size) { | |
155 | ea_size = le32_to_cpu(ef->size); | |
156 | if (ea_size > bytes) | |
157 | goto out1; | |
158 | continue; | |
159 | } | |
160 | ||
161 | /* Check if we can use fields ef->name_len and ef->elength. */ | |
162 | if (bytes < offsetof(struct EA_FULL, name)) | |
163 | goto out1; | |
164 | ||
165 | ea_size = ALIGN(struct_size(ef, name, | |
166 | 1 + ef->name_len + | |
167 | le16_to_cpu(ef->elength)), | |
168 | 4); | |
169 | if (ea_size > bytes) | |
170 | goto out1; | |
171 | } | |
172 | ||
be71b5cb KK |
173 | *ea = ea_p; |
174 | return 0; | |
175 | ||
0e8235d2 | 176 | out1: |
195c52bd | 177 | kfree(ea_p); |
0e8235d2 KK |
178 | out: |
179 | ntfs_set_state(sbi, NTFS_DIRTY_DIRTY); | |
be71b5cb KK |
180 | return err; |
181 | } | |
182 | ||
183 | /* | |
184 | * ntfs_list_ea | |
185 | * | |
e8b8e97f KA |
186 | * Copy a list of xattrs names into the buffer |
187 | * provided, or compute the buffer size required. | |
be71b5cb | 188 | * |
e8b8e97f KA |
189 | * Return: |
190 | * * Number of bytes used / required on | |
191 | * * -ERRNO - on failure | |
be71b5cb KK |
192 | */ |
193 | static ssize_t ntfs_list_ea(struct ntfs_inode *ni, char *buffer, | |
194 | size_t bytes_per_buffer) | |
195 | { | |
196 | const struct EA_INFO *info; | |
197 | struct EA_FULL *ea_all = NULL; | |
198 | const struct EA_FULL *ea; | |
199 | u32 off, size; | |
200 | int err; | |
0e8235d2 | 201 | int ea_size; |
be71b5cb KK |
202 | size_t ret; |
203 | ||
204 | err = ntfs_read_ea(ni, &ea_all, 0, &info); | |
205 | if (err) | |
206 | return err; | |
207 | ||
208 | if (!info || !ea_all) | |
209 | return 0; | |
210 | ||
211 | size = le32_to_cpu(info->size); | |
212 | ||
e8b8e97f | 213 | /* Enumerate all xattrs. */ |
8e7e27b2 ZH |
214 | ret = 0; |
215 | for (off = 0; off + sizeof(struct EA_FULL) < size; off += ea_size) { | |
be71b5cb | 216 | ea = Add2Ptr(ea_all, off); |
0e8235d2 | 217 | ea_size = unpacked_ea_size(ea); |
be71b5cb | 218 | |
3c675ddf ZH |
219 | if (!ea->name_len) |
220 | break; | |
221 | ||
731ab1f9 EAD |
222 | if (ea->name_len > ea_size) |
223 | break; | |
224 | ||
be71b5cb | 225 | if (buffer) { |
8e7e27b2 ZH |
226 | /* Check if we can use field ea->name */ |
227 | if (off + ea_size > size) | |
228 | break; | |
229 | ||
be71b5cb KK |
230 | if (ret + ea->name_len + 1 > bytes_per_buffer) { |
231 | err = -ERANGE; | |
232 | goto out; | |
233 | } | |
234 | ||
235 | memcpy(buffer + ret, ea->name, ea->name_len); | |
236 | buffer[ret + ea->name_len] = 0; | |
237 | } | |
238 | ||
239 | ret += ea->name_len + 1; | |
240 | } | |
241 | ||
242 | out: | |
195c52bd | 243 | kfree(ea_all); |
be71b5cb KK |
244 | return err ? err : ret; |
245 | } | |
246 | ||
247 | static int ntfs_get_ea(struct inode *inode, const char *name, size_t name_len, | |
248 | void *buffer, size_t size, size_t *required) | |
249 | { | |
250 | struct ntfs_inode *ni = ntfs_i(inode); | |
251 | const struct EA_INFO *info; | |
252 | struct EA_FULL *ea_all = NULL; | |
253 | const struct EA_FULL *ea; | |
254 | u32 off, len; | |
255 | int err; | |
256 | ||
257 | if (!(ni->ni_flags & NI_FLAG_EA)) | |
258 | return -ENODATA; | |
259 | ||
260 | if (!required) | |
261 | ni_lock(ni); | |
262 | ||
263 | len = 0; | |
264 | ||
265 | if (name_len > 255) { | |
266 | err = -ENAMETOOLONG; | |
267 | goto out; | |
268 | } | |
269 | ||
270 | err = ntfs_read_ea(ni, &ea_all, 0, &info); | |
271 | if (err) | |
272 | goto out; | |
273 | ||
274 | if (!info) | |
275 | goto out; | |
276 | ||
e8b8e97f | 277 | /* Enumerate all xattrs. */ |
0e8235d2 KK |
278 | if (!find_ea(ea_all, le32_to_cpu(info->size), name, name_len, &off, |
279 | NULL)) { | |
be71b5cb KK |
280 | err = -ENODATA; |
281 | goto out; | |
282 | } | |
283 | ea = Add2Ptr(ea_all, off); | |
284 | ||
285 | len = le16_to_cpu(ea->elength); | |
286 | if (!buffer) { | |
287 | err = 0; | |
288 | goto out; | |
289 | } | |
290 | ||
291 | if (len > size) { | |
292 | err = -ERANGE; | |
293 | if (required) | |
294 | *required = len; | |
295 | goto out; | |
296 | } | |
297 | ||
298 | memcpy(buffer, ea->name + ea->name_len + 1, len); | |
299 | err = 0; | |
300 | ||
301 | out: | |
195c52bd | 302 | kfree(ea_all); |
be71b5cb KK |
303 | if (!required) |
304 | ni_unlock(ni); | |
305 | ||
306 | return err ? err : len; | |
307 | } | |
308 | ||
309 | static noinline int ntfs_set_ea(struct inode *inode, const char *name, | |
310 | size_t name_len, const void *value, | |
1842fbc8 KK |
311 | size_t val_size, int flags, bool locked, |
312 | __le16 *ea_size) | |
be71b5cb KK |
313 | { |
314 | struct ntfs_inode *ni = ntfs_i(inode); | |
315 | struct ntfs_sb_info *sbi = ni->mi.sbi; | |
316 | int err; | |
317 | struct EA_INFO ea_info; | |
318 | const struct EA_INFO *info; | |
319 | struct EA_FULL *new_ea; | |
320 | struct EA_FULL *ea_all = NULL; | |
321 | size_t add, new_pack; | |
0e8235d2 | 322 | u32 off, size, ea_sz; |
be71b5cb KK |
323 | __le16 size_pack; |
324 | struct ATTRIB *attr; | |
325 | struct ATTR_LIST_ENTRY *le; | |
326 | struct mft_inode *mi; | |
327 | struct runs_tree ea_run; | |
328 | u64 new_sz; | |
329 | void *p; | |
330 | ||
3a2154b2 KK |
331 | if (!locked) |
332 | ni_lock(ni); | |
be71b5cb KK |
333 | |
334 | run_init(&ea_run); | |
335 | ||
336 | if (name_len > 255) { | |
337 | err = -ENAMETOOLONG; | |
338 | goto out; | |
339 | } | |
340 | ||
fa3cacf5 | 341 | add = ALIGN(struct_size(ea_all, name, 1 + name_len + val_size), 4); |
be71b5cb KK |
342 | |
343 | err = ntfs_read_ea(ni, &ea_all, add, &info); | |
344 | if (err) | |
345 | goto out; | |
346 | ||
347 | if (!info) { | |
348 | memset(&ea_info, 0, sizeof(ea_info)); | |
349 | size = 0; | |
350 | size_pack = 0; | |
351 | } else { | |
352 | memcpy(&ea_info, info, sizeof(ea_info)); | |
353 | size = le32_to_cpu(ea_info.size); | |
354 | size_pack = ea_info.size_pack; | |
355 | } | |
356 | ||
0e8235d2 | 357 | if (info && find_ea(ea_all, size, name, name_len, &off, &ea_sz)) { |
be71b5cb | 358 | struct EA_FULL *ea; |
be71b5cb KK |
359 | |
360 | if (flags & XATTR_CREATE) { | |
361 | err = -EEXIST; | |
362 | goto out; | |
363 | } | |
364 | ||
365 | ea = Add2Ptr(ea_all, off); | |
366 | ||
367 | /* | |
368 | * Check simple case when we try to insert xattr with the same value | |
369 | * e.g. ntfs_save_wsl_perm | |
370 | */ | |
371 | if (val_size && le16_to_cpu(ea->elength) == val_size && | |
372 | !memcmp(ea->name + ea->name_len + 1, value, val_size)) { | |
e8b8e97f | 373 | /* xattr already contains the required value. */ |
be71b5cb KK |
374 | goto out; |
375 | } | |
376 | ||
e8b8e97f | 377 | /* Remove current xattr. */ |
be71b5cb KK |
378 | if (ea->flags & FILE_NEED_EA) |
379 | le16_add_cpu(&ea_info.count, -1); | |
380 | ||
be71b5cb KK |
381 | le16_add_cpu(&ea_info.size_pack, 0 - packed_ea_size(ea)); |
382 | ||
383 | memmove(ea, Add2Ptr(ea, ea_sz), size - off - ea_sz); | |
384 | ||
385 | size -= ea_sz; | |
386 | memset(Add2Ptr(ea_all, size), 0, ea_sz); | |
387 | ||
388 | ea_info.size = cpu_to_le32(size); | |
389 | ||
390 | if ((flags & XATTR_REPLACE) && !val_size) { | |
e8b8e97f | 391 | /* Remove xattr. */ |
be71b5cb KK |
392 | goto update_ea; |
393 | } | |
394 | } else { | |
395 | if (flags & XATTR_REPLACE) { | |
396 | err = -ENODATA; | |
397 | goto out; | |
398 | } | |
399 | ||
400 | if (!ea_all) { | |
195c52bd | 401 | ea_all = kzalloc(add, GFP_NOFS); |
be71b5cb KK |
402 | if (!ea_all) { |
403 | err = -ENOMEM; | |
404 | goto out; | |
405 | } | |
406 | } | |
407 | } | |
408 | ||
e8b8e97f | 409 | /* Append new xattr. */ |
be71b5cb KK |
410 | new_ea = Add2Ptr(ea_all, size); |
411 | new_ea->size = cpu_to_le32(add); | |
412 | new_ea->flags = 0; | |
413 | new_ea->name_len = name_len; | |
414 | new_ea->elength = cpu_to_le16(val_size); | |
415 | memcpy(new_ea->name, name, name_len); | |
416 | new_ea->name[name_len] = 0; | |
417 | memcpy(new_ea->name + name_len + 1, value, val_size); | |
418 | new_pack = le16_to_cpu(ea_info.size_pack) + packed_ea_size(new_ea); | |
be71b5cb | 419 | ea_info.size_pack = cpu_to_le16(new_pack); |
e8b8e97f | 420 | /* New size of ATTR_EA. */ |
be71b5cb | 421 | size += add; |
cff32466 KK |
422 | ea_info.size = cpu_to_le32(size); |
423 | ||
424 | /* | |
425 | * 1. Check ea_info.size_pack for overflow. | |
e479f0a6 | 426 | * 2. New attribute size must fit value from $AttrDef |
cff32466 KK |
427 | */ |
428 | if (new_pack > 0xffff || size > sbi->ea_max_size) { | |
429 | ntfs_inode_warn( | |
430 | inode, | |
431 | "The size of extended attributes must not exceed 64KiB"); | |
be71b5cb KK |
432 | err = -EFBIG; // -EINVAL? |
433 | goto out; | |
434 | } | |
be71b5cb KK |
435 | |
436 | update_ea: | |
437 | ||
438 | if (!info) { | |
e8b8e97f | 439 | /* Create xattr. */ |
be71b5cb KK |
440 | if (!size) { |
441 | err = 0; | |
442 | goto out; | |
443 | } | |
444 | ||
445 | err = ni_insert_resident(ni, sizeof(struct EA_INFO), | |
78ab59fe KK |
446 | ATTR_EA_INFO, NULL, 0, NULL, NULL, |
447 | NULL); | |
be71b5cb KK |
448 | if (err) |
449 | goto out; | |
450 | ||
78ab59fe KK |
451 | err = ni_insert_resident(ni, 0, ATTR_EA, NULL, 0, NULL, NULL, |
452 | NULL); | |
be71b5cb KK |
453 | if (err) |
454 | goto out; | |
455 | } | |
456 | ||
457 | new_sz = size; | |
458 | err = attr_set_size(ni, ATTR_EA, NULL, 0, &ea_run, new_sz, &new_sz, | |
459 | false, NULL); | |
460 | if (err) | |
461 | goto out; | |
462 | ||
463 | le = NULL; | |
464 | attr = ni_find_attr(ni, NULL, &le, ATTR_EA_INFO, NULL, 0, NULL, &mi); | |
465 | if (!attr) { | |
466 | err = -EINVAL; | |
467 | goto out; | |
468 | } | |
469 | ||
470 | if (!size) { | |
e8b8e97f | 471 | /* Delete xattr, ATTR_EA_INFO */ |
78ab59fe | 472 | ni_remove_attr_le(ni, attr, mi, le); |
be71b5cb KK |
473 | } else { |
474 | p = resident_data_ex(attr, sizeof(struct EA_INFO)); | |
475 | if (!p) { | |
476 | err = -EINVAL; | |
477 | goto out; | |
478 | } | |
479 | memcpy(p, &ea_info, sizeof(struct EA_INFO)); | |
480 | mi->dirty = true; | |
481 | } | |
482 | ||
483 | le = NULL; | |
484 | attr = ni_find_attr(ni, NULL, &le, ATTR_EA, NULL, 0, NULL, &mi); | |
485 | if (!attr) { | |
486 | err = -EINVAL; | |
487 | goto out; | |
488 | } | |
489 | ||
490 | if (!size) { | |
e8b8e97f | 491 | /* Delete xattr, ATTR_EA */ |
78ab59fe | 492 | ni_remove_attr_le(ni, attr, mi, le); |
be71b5cb | 493 | } else if (attr->non_res) { |
42f86b12 KK |
494 | err = attr_load_runs_range(ni, ATTR_EA, NULL, 0, &ea_run, 0, |
495 | size); | |
496 | if (err) | |
497 | goto out; | |
498 | ||
63544672 | 499 | err = ntfs_sb_write_run(sbi, &ea_run, 0, ea_all, size, 0); |
be71b5cb KK |
500 | if (err) |
501 | goto out; | |
502 | } else { | |
503 | p = resident_data_ex(attr, size); | |
504 | if (!p) { | |
505 | err = -EINVAL; | |
506 | goto out; | |
507 | } | |
508 | memcpy(p, ea_all, size); | |
509 | mi->dirty = true; | |
510 | } | |
511 | ||
e8b8e97f | 512 | /* Check if we delete the last xattr. */ |
be71b5cb KK |
513 | if (size) |
514 | ni->ni_flags |= NI_FLAG_EA; | |
515 | else | |
516 | ni->ni_flags &= ~NI_FLAG_EA; | |
517 | ||
518 | if (ea_info.size_pack != size_pack) | |
519 | ni->ni_flags |= NI_FLAG_UPDATE_PARENT; | |
1842fbc8 KK |
520 | if (ea_size) |
521 | *ea_size = ea_info.size_pack; | |
be71b5cb KK |
522 | mark_inode_dirty(&ni->vfs_inode); |
523 | ||
524 | out: | |
3a2154b2 KK |
525 | if (!locked) |
526 | ni_unlock(ni); | |
be71b5cb KK |
527 | |
528 | run_close(&ea_run); | |
195c52bd | 529 | kfree(ea_all); |
be71b5cb KK |
530 | |
531 | return err; | |
532 | } | |
533 | ||
534 | #ifdef CONFIG_NTFS3_FS_POSIX_ACL | |
75c5e0c9 KK |
535 | |
536 | /* | |
537 | * ntfs_get_acl - inode_operations::get_acl | |
538 | */ | |
f0377761 KK |
539 | struct posix_acl *ntfs_get_acl(struct mnt_idmap *idmap, struct dentry *dentry, |
540 | int type) | |
be71b5cb | 541 | { |
75c5e0c9 | 542 | struct inode *inode = d_inode(dentry); |
be71b5cb KK |
543 | struct ntfs_inode *ni = ntfs_i(inode); |
544 | const char *name; | |
545 | size_t name_len; | |
546 | struct posix_acl *acl; | |
547 | size_t req; | |
548 | int err; | |
549 | void *buf; | |
550 | ||
e8b8e97f | 551 | /* Allocate PATH_MAX bytes. */ |
be71b5cb KK |
552 | buf = __getname(); |
553 | if (!buf) | |
554 | return ERR_PTR(-ENOMEM); | |
555 | ||
e8b8e97f | 556 | /* Possible values of 'type' was already checked above. */ |
be71b5cb KK |
557 | if (type == ACL_TYPE_ACCESS) { |
558 | name = XATTR_NAME_POSIX_ACL_ACCESS; | |
559 | name_len = sizeof(XATTR_NAME_POSIX_ACL_ACCESS) - 1; | |
560 | } else { | |
561 | name = XATTR_NAME_POSIX_ACL_DEFAULT; | |
562 | name_len = sizeof(XATTR_NAME_POSIX_ACL_DEFAULT) - 1; | |
563 | } | |
564 | ||
75c5e0c9 | 565 | ni_lock(ni); |
be71b5cb KK |
566 | |
567 | err = ntfs_get_ea(inode, name, name_len, buf, PATH_MAX, &req); | |
568 | ||
75c5e0c9 | 569 | ni_unlock(ni); |
be71b5cb | 570 | |
e8b8e97f | 571 | /* Translate extended attribute to acl. */ |
2926e429 | 572 | if (err >= 0) { |
0c3bc789 | 573 | acl = posix_acl_from_xattr(&init_user_ns, buf, err); |
0bd5fdb8 KK |
574 | } else if (err == -ENODATA) { |
575 | acl = NULL; | |
be71b5cb | 576 | } else { |
0bd5fdb8 | 577 | acl = ERR_PTR(err); |
be71b5cb KK |
578 | } |
579 | ||
0bd5fdb8 KK |
580 | if (!IS_ERR(acl)) |
581 | set_cached_acl(inode, type, acl); | |
582 | ||
be71b5cb KK |
583 | __putname(buf); |
584 | ||
585 | return acl; | |
586 | } | |
587 | ||
700b7940 | 588 | static noinline int ntfs_set_acl_ex(struct mnt_idmap *idmap, |
be71b5cb | 589 | struct inode *inode, struct posix_acl *acl, |
9186d472 | 590 | int type, bool init_acl) |
be71b5cb KK |
591 | { |
592 | const char *name; | |
593 | size_t size, name_len; | |
460bbf29 KK |
594 | void *value; |
595 | int err; | |
398c35f4 | 596 | int flags; |
460bbf29 | 597 | umode_t mode; |
be71b5cb KK |
598 | |
599 | if (S_ISLNK(inode->i_mode)) | |
600 | return -EOPNOTSUPP; | |
601 | ||
460bbf29 | 602 | mode = inode->i_mode; |
be71b5cb KK |
603 | switch (type) { |
604 | case ACL_TYPE_ACCESS: | |
9186d472 KK |
605 | /* Do not change i_mode if we are in init_acl */ |
606 | if (acl && !init_acl) { | |
f0377761 | 607 | err = posix_acl_update_mode(idmap, inode, &mode, &acl); |
ba77237e | 608 | if (err) |
d4073595 | 609 | return err; |
be71b5cb KK |
610 | } |
611 | name = XATTR_NAME_POSIX_ACL_ACCESS; | |
612 | name_len = sizeof(XATTR_NAME_POSIX_ACL_ACCESS) - 1; | |
613 | break; | |
614 | ||
615 | case ACL_TYPE_DEFAULT: | |
616 | if (!S_ISDIR(inode->i_mode)) | |
617 | return acl ? -EACCES : 0; | |
618 | name = XATTR_NAME_POSIX_ACL_DEFAULT; | |
619 | name_len = sizeof(XATTR_NAME_POSIX_ACL_DEFAULT) - 1; | |
620 | break; | |
621 | ||
622 | default: | |
623 | return -EINVAL; | |
624 | } | |
625 | ||
626 | if (!acl) { | |
398c35f4 | 627 | /* Remove xattr if it can be presented via mode. */ |
be71b5cb KK |
628 | size = 0; |
629 | value = NULL; | |
398c35f4 | 630 | flags = XATTR_REPLACE; |
be71b5cb KK |
631 | } else { |
632 | size = posix_acl_xattr_size(acl->a_count); | |
195c52bd | 633 | value = kmalloc(size, GFP_NOFS); |
be71b5cb KK |
634 | if (!value) |
635 | return -ENOMEM; | |
0c3bc789 | 636 | err = posix_acl_to_xattr(&init_user_ns, acl, value, size); |
be71b5cb KK |
637 | if (err < 0) |
638 | goto out; | |
398c35f4 | 639 | flags = 0; |
be71b5cb KK |
640 | } |
641 | ||
1842fbc8 | 642 | err = ntfs_set_ea(inode, name, name_len, value, size, flags, 0, NULL); |
398c35f4 KK |
643 | if (err == -ENODATA && !size) |
644 | err = 0; /* Removing non existed xattr. */ | |
460bbf29 | 645 | if (!err) { |
be71b5cb | 646 | set_cached_acl(inode, type, acl); |
e31195a3 | 647 | inode->i_mode = mode; |
3d65c46f | 648 | inode_set_ctime_current(inode); |
e31195a3 | 649 | mark_inode_dirty(inode); |
460bbf29 | 650 | } |
be71b5cb KK |
651 | |
652 | out: | |
195c52bd | 653 | kfree(value); |
be71b5cb KK |
654 | |
655 | return err; | |
656 | } | |
657 | ||
658 | /* | |
e8b8e97f | 659 | * ntfs_set_acl - inode_operations::set_acl |
be71b5cb | 660 | */ |
13e83a49 | 661 | int ntfs_set_acl(struct mnt_idmap *idmap, struct dentry *dentry, |
be71b5cb KK |
662 | struct posix_acl *acl, int type) |
663 | { | |
700b7940 | 664 | return ntfs_set_acl_ex(idmap, d_inode(dentry), acl, type, false); |
be71b5cb KK |
665 | } |
666 | ||
667 | /* | |
e8b8e97f KA |
668 | * ntfs_init_acl - Initialize the ACLs of a new inode. |
669 | * | |
670 | * Called from ntfs_create_inode(). | |
be71b5cb | 671 | */ |
700b7940 | 672 | int ntfs_init_acl(struct mnt_idmap *idmap, struct inode *inode, |
be71b5cb KK |
673 | struct inode *dir) |
674 | { | |
675 | struct posix_acl *default_acl, *acl; | |
676 | int err; | |
677 | ||
66019837 KK |
678 | err = posix_acl_create(dir, &inode->i_mode, &default_acl, &acl); |
679 | if (err) | |
680 | return err; | |
be71b5cb | 681 | |
66019837 | 682 | if (default_acl) { |
700b7940 | 683 | err = ntfs_set_acl_ex(idmap, inode, default_acl, |
9186d472 | 684 | ACL_TYPE_DEFAULT, true); |
be71b5cb | 685 | posix_acl_release(default_acl); |
66019837 KK |
686 | } else { |
687 | inode->i_default_acl = NULL; | |
be71b5cb KK |
688 | } |
689 | ||
19e890ff | 690 | if (acl) { |
66019837 | 691 | if (!err) |
700b7940 | 692 | err = ntfs_set_acl_ex(idmap, inode, acl, |
9186d472 | 693 | ACL_TYPE_ACCESS, true); |
66019837 | 694 | posix_acl_release(acl); |
19e890ff YX |
695 | } else { |
696 | inode->i_acl = NULL; | |
66019837 | 697 | } |
be71b5cb | 698 | |
be71b5cb KK |
699 | return err; |
700 | } | |
701 | #endif | |
702 | ||
703 | /* | |
e8b8e97f | 704 | * ntfs_acl_chmod - Helper for ntfs3_setattr(). |
be71b5cb | 705 | */ |
13e83a49 | 706 | int ntfs_acl_chmod(struct mnt_idmap *idmap, struct dentry *dentry) |
be71b5cb | 707 | { |
138060ba | 708 | struct inode *inode = d_inode(dentry); |
be71b5cb KK |
709 | struct super_block *sb = inode->i_sb; |
710 | ||
711 | if (!(sb->s_flags & SB_POSIXACL)) | |
712 | return 0; | |
713 | ||
714 | if (S_ISLNK(inode->i_mode)) | |
715 | return -EOPNOTSUPP; | |
716 | ||
13e83a49 | 717 | return posix_acl_chmod(idmap, dentry, inode->i_mode); |
be71b5cb KK |
718 | } |
719 | ||
be71b5cb | 720 | /* |
e8b8e97f | 721 | * ntfs_listxattr - inode_operations::listxattr |
be71b5cb KK |
722 | */ |
723 | ssize_t ntfs_listxattr(struct dentry *dentry, char *buffer, size_t size) | |
724 | { | |
725 | struct inode *inode = d_inode(dentry); | |
726 | struct ntfs_inode *ni = ntfs_i(inode); | |
727 | ssize_t ret; | |
728 | ||
729 | if (!(ni->ni_flags & NI_FLAG_EA)) { | |
730 | /* no xattr in file */ | |
731 | return 0; | |
732 | } | |
733 | ||
734 | ni_lock(ni); | |
735 | ||
736 | ret = ntfs_list_ea(ni, buffer, size); | |
737 | ||
738 | ni_unlock(ni); | |
739 | ||
740 | return ret; | |
741 | } | |
742 | ||
743 | static int ntfs_getxattr(const struct xattr_handler *handler, struct dentry *de, | |
744 | struct inode *inode, const char *name, void *buffer, | |
745 | size_t size) | |
746 | { | |
747 | int err; | |
748 | struct ntfs_inode *ni = ntfs_i(inode); | |
be71b5cb | 749 | |
6c3684e7 KK |
750 | if (unlikely(ntfs3_forced_shutdown(inode->i_sb))) |
751 | return -EIO; | |
752 | ||
e8b8e97f | 753 | /* Dispatch request. */ |
d45da67c | 754 | if (!strcmp(name, SYSTEM_DOS_ATTRIB)) { |
be71b5cb KK |
755 | /* system.dos_attrib */ |
756 | if (!buffer) { | |
757 | err = sizeof(u8); | |
758 | } else if (size < sizeof(u8)) { | |
759 | err = -ENODATA; | |
760 | } else { | |
761 | err = sizeof(u8); | |
762 | *(u8 *)buffer = le32_to_cpu(ni->std_fa); | |
763 | } | |
764 | goto out; | |
765 | } | |
766 | ||
0d19f3d7 DP |
767 | if (!strcmp(name, SYSTEM_NTFS_ATTRIB) || |
768 | !strcmp(name, SYSTEM_NTFS_ATTRIB_BE)) { | |
be71b5cb KK |
769 | /* system.ntfs_attrib */ |
770 | if (!buffer) { | |
771 | err = sizeof(u32); | |
772 | } else if (size < sizeof(u32)) { | |
773 | err = -ENODATA; | |
774 | } else { | |
775 | err = sizeof(u32); | |
776 | *(u32 *)buffer = le32_to_cpu(ni->std_fa); | |
0d19f3d7 | 777 | if (!strcmp(name, SYSTEM_NTFS_ATTRIB_BE)) |
0203471d | 778 | *(__be32 *)buffer = cpu_to_be32(*(u32 *)buffer); |
be71b5cb KK |
779 | } |
780 | goto out; | |
781 | } | |
782 | ||
d45da67c | 783 | if (!strcmp(name, SYSTEM_NTFS_SECURITY)) { |
be71b5cb KK |
784 | /* system.ntfs_security*/ |
785 | struct SECURITY_DESCRIPTOR_RELATIVE *sd = NULL; | |
786 | size_t sd_size = 0; | |
787 | ||
788 | if (!is_ntfs3(ni->mi.sbi)) { | |
e8b8e97f | 789 | /* We should get nt4 security. */ |
be71b5cb KK |
790 | err = -EINVAL; |
791 | goto out; | |
792 | } else if (le32_to_cpu(ni->std_security_id) < | |
793 | SECURITY_ID_FIRST) { | |
794 | err = -ENOENT; | |
795 | goto out; | |
796 | } | |
797 | ||
798 | err = ntfs_get_security_by_id(ni->mi.sbi, ni->std_security_id, | |
799 | &sd, &sd_size); | |
800 | if (err) | |
801 | goto out; | |
802 | ||
803 | if (!is_sd_valid(sd, sd_size)) { | |
804 | ntfs_inode_warn( | |
805 | inode, | |
806 | "looks like you get incorrect security descriptor id=%u", | |
807 | ni->std_security_id); | |
808 | } | |
809 | ||
810 | if (!buffer) { | |
811 | err = sd_size; | |
812 | } else if (size < sd_size) { | |
813 | err = -ENODATA; | |
814 | } else { | |
815 | err = sd_size; | |
816 | memcpy(buffer, sd, sd_size); | |
817 | } | |
195c52bd | 818 | kfree(sd); |
be71b5cb KK |
819 | goto out; |
820 | } | |
821 | ||
e8b8e97f | 822 | /* Deal with NTFS extended attribute. */ |
d45da67c | 823 | err = ntfs_get_ea(inode, name, strlen(name), buffer, size, NULL); |
be71b5cb KK |
824 | |
825 | out: | |
826 | return err; | |
827 | } | |
828 | ||
829 | /* | |
e8b8e97f | 830 | * ntfs_setxattr - inode_operations::setxattr |
be71b5cb KK |
831 | */ |
832 | static noinline int ntfs_setxattr(const struct xattr_handler *handler, | |
f0377761 KK |
833 | struct mnt_idmap *idmap, struct dentry *de, |
834 | struct inode *inode, const char *name, | |
835 | const void *value, size_t size, int flags) | |
be71b5cb KK |
836 | { |
837 | int err = -EINVAL; | |
838 | struct ntfs_inode *ni = ntfs_i(inode); | |
be71b5cb KK |
839 | enum FILE_ATTRIBUTE new_fa; |
840 | ||
e8b8e97f | 841 | /* Dispatch request. */ |
d45da67c | 842 | if (!strcmp(name, SYSTEM_DOS_ATTRIB)) { |
be71b5cb KK |
843 | if (sizeof(u8) != size) |
844 | goto out; | |
845 | new_fa = cpu_to_le32(*(u8 *)value); | |
846 | goto set_new_fa; | |
847 | } | |
848 | ||
0d19f3d7 DP |
849 | if (!strcmp(name, SYSTEM_NTFS_ATTRIB) || |
850 | !strcmp(name, SYSTEM_NTFS_ATTRIB_BE)) { | |
be71b5cb KK |
851 | if (size != sizeof(u32)) |
852 | goto out; | |
0d19f3d7 | 853 | if (!strcmp(name, SYSTEM_NTFS_ATTRIB_BE)) |
0203471d | 854 | new_fa = cpu_to_le32(be32_to_cpu(*(__be32 *)value)); |
0d19f3d7 DP |
855 | else |
856 | new_fa = cpu_to_le32(*(u32 *)value); | |
be71b5cb KK |
857 | |
858 | if (S_ISREG(inode->i_mode)) { | |
e8b8e97f | 859 | /* Process compressed/sparsed in special way. */ |
be71b5cb KK |
860 | ni_lock(ni); |
861 | err = ni_new_attr_flags(ni, new_fa); | |
862 | ni_unlock(ni); | |
863 | if (err) | |
864 | goto out; | |
865 | } | |
866 | set_new_fa: | |
867 | /* | |
868 | * Thanks Mark Harmstone: | |
e8b8e97f | 869 | * Keep directory bit consistency. |
be71b5cb KK |
870 | */ |
871 | if (S_ISDIR(inode->i_mode)) | |
872 | new_fa |= FILE_ATTRIBUTE_DIRECTORY; | |
873 | else | |
874 | new_fa &= ~FILE_ATTRIBUTE_DIRECTORY; | |
875 | ||
876 | if (ni->std_fa != new_fa) { | |
877 | ni->std_fa = new_fa; | |
878 | if (new_fa & FILE_ATTRIBUTE_READONLY) | |
879 | inode->i_mode &= ~0222; | |
880 | else | |
881 | inode->i_mode |= 0222; | |
e8b8e97f | 882 | /* Std attribute always in primary record. */ |
be71b5cb KK |
883 | ni->mi.dirty = true; |
884 | mark_inode_dirty(inode); | |
885 | } | |
886 | err = 0; | |
887 | ||
888 | goto out; | |
889 | } | |
890 | ||
d45da67c | 891 | if (!strcmp(name, SYSTEM_NTFS_SECURITY)) { |
be71b5cb KK |
892 | /* system.ntfs_security*/ |
893 | __le32 security_id; | |
894 | bool inserted; | |
895 | struct ATTR_STD_INFO5 *std; | |
896 | ||
897 | if (!is_ntfs3(ni->mi.sbi)) { | |
898 | /* | |
e8b8e97f KA |
899 | * We should replace ATTR_SECURE. |
900 | * Skip this way cause it is nt4 feature. | |
be71b5cb KK |
901 | */ |
902 | err = -EINVAL; | |
903 | goto out; | |
904 | } | |
905 | ||
906 | if (!is_sd_valid(value, size)) { | |
907 | err = -EINVAL; | |
908 | ntfs_inode_warn( | |
909 | inode, | |
910 | "you try to set invalid security descriptor"); | |
911 | goto out; | |
912 | } | |
913 | ||
914 | err = ntfs_insert_security(ni->mi.sbi, value, size, | |
915 | &security_id, &inserted); | |
916 | if (err) | |
917 | goto out; | |
918 | ||
919 | ni_lock(ni); | |
920 | std = ni_std5(ni); | |
921 | if (!std) { | |
922 | err = -EINVAL; | |
923 | } else if (std->security_id != security_id) { | |
924 | std->security_id = ni->std_security_id = security_id; | |
e8b8e97f | 925 | /* Std attribute always in primary record. */ |
be71b5cb KK |
926 | ni->mi.dirty = true; |
927 | mark_inode_dirty(&ni->vfs_inode); | |
928 | } | |
929 | ni_unlock(ni); | |
930 | goto out; | |
931 | } | |
932 | ||
e8b8e97f | 933 | /* Deal with NTFS extended attribute. */ |
1842fbc8 KK |
934 | err = ntfs_set_ea(inode, name, strlen(name), value, size, flags, 0, |
935 | NULL); | |
be71b5cb KK |
936 | |
937 | out: | |
3d65c46f | 938 | inode_set_ctime_current(inode); |
2d44667c KK |
939 | mark_inode_dirty(inode); |
940 | ||
be71b5cb KK |
941 | return err; |
942 | } | |
943 | ||
944 | /* | |
945 | * ntfs_save_wsl_perm | |
946 | * | |
947 | * save uid/gid/mode in xattr | |
948 | */ | |
1842fbc8 | 949 | int ntfs_save_wsl_perm(struct inode *inode, __le16 *ea_size) |
be71b5cb KK |
950 | { |
951 | int err; | |
952 | __le32 value; | |
3a2154b2 | 953 | struct ntfs_inode *ni = ntfs_i(inode); |
be71b5cb | 954 | |
3a2154b2 | 955 | ni_lock(ni); |
be71b5cb KK |
956 | value = cpu_to_le32(i_uid_read(inode)); |
957 | err = ntfs_set_ea(inode, "$LXUID", sizeof("$LXUID") - 1, &value, | |
1842fbc8 | 958 | sizeof(value), 0, true, ea_size); |
be71b5cb KK |
959 | if (err) |
960 | goto out; | |
961 | ||
962 | value = cpu_to_le32(i_gid_read(inode)); | |
963 | err = ntfs_set_ea(inode, "$LXGID", sizeof("$LXGID") - 1, &value, | |
1842fbc8 | 964 | sizeof(value), 0, true, ea_size); |
be71b5cb KK |
965 | if (err) |
966 | goto out; | |
967 | ||
968 | value = cpu_to_le32(inode->i_mode); | |
969 | err = ntfs_set_ea(inode, "$LXMOD", sizeof("$LXMOD") - 1, &value, | |
1842fbc8 | 970 | sizeof(value), 0, true, ea_size); |
be71b5cb KK |
971 | if (err) |
972 | goto out; | |
973 | ||
974 | if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) { | |
975 | value = cpu_to_le32(inode->i_rdev); | |
976 | err = ntfs_set_ea(inode, "$LXDEV", sizeof("$LXDEV") - 1, &value, | |
1842fbc8 | 977 | sizeof(value), 0, true, ea_size); |
be71b5cb KK |
978 | if (err) |
979 | goto out; | |
980 | } | |
981 | ||
982 | out: | |
3a2154b2 | 983 | ni_unlock(ni); |
be71b5cb KK |
984 | /* In case of error should we delete all WSL xattr? */ |
985 | return err; | |
986 | } | |
987 | ||
988 | /* | |
989 | * ntfs_get_wsl_perm | |
990 | * | |
991 | * get uid/gid/mode from xattr | |
992 | * it is called from ntfs_iget5->ntfs_read_mft | |
993 | */ | |
994 | void ntfs_get_wsl_perm(struct inode *inode) | |
995 | { | |
996 | size_t sz; | |
997 | __le32 value[3]; | |
998 | ||
999 | if (ntfs_get_ea(inode, "$LXUID", sizeof("$LXUID") - 1, &value[0], | |
1000 | sizeof(value[0]), &sz) == sizeof(value[0]) && | |
1001 | ntfs_get_ea(inode, "$LXGID", sizeof("$LXGID") - 1, &value[1], | |
1002 | sizeof(value[1]), &sz) == sizeof(value[1]) && | |
1003 | ntfs_get_ea(inode, "$LXMOD", sizeof("$LXMOD") - 1, &value[2], | |
1004 | sizeof(value[2]), &sz) == sizeof(value[2])) { | |
1005 | i_uid_write(inode, (uid_t)le32_to_cpu(value[0])); | |
1006 | i_gid_write(inode, (gid_t)le32_to_cpu(value[1])); | |
1007 | inode->i_mode = le32_to_cpu(value[2]); | |
1008 | ||
1009 | if (ntfs_get_ea(inode, "$LXDEV", sizeof("$$LXDEV") - 1, | |
1010 | &value[0], sizeof(value), | |
1011 | &sz) == sizeof(value[0])) { | |
1012 | inode->i_rdev = le32_to_cpu(value[0]); | |
1013 | } | |
1014 | } | |
1015 | } | |
1016 | ||
1017 | static bool ntfs_xattr_user_list(struct dentry *dentry) | |
1018 | { | |
1019 | return true; | |
1020 | } | |
1021 | ||
1022 | // clang-format off | |
a26aa123 | 1023 | static const struct xattr_handler ntfs_other_xattr_handler = { |
be71b5cb KK |
1024 | .prefix = "", |
1025 | .get = ntfs_getxattr, | |
1026 | .set = ntfs_setxattr, | |
1027 | .list = ntfs_xattr_user_list, | |
1028 | }; | |
1029 | ||
5bf1dd94 | 1030 | const struct xattr_handler * const ntfs_xattr_handlers[] = { |
a26aa123 | 1031 | &ntfs_other_xattr_handler, |
be71b5cb KK |
1032 | NULL, |
1033 | }; | |
1034 | // clang-format on |