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