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