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