162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (C) 2019-2021 Paragon Software GmbH, All rights reserved. 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/fs.h> 962306a36Sopenharmony_ci#include <linux/posix_acl.h> 1062306a36Sopenharmony_ci#include <linux/posix_acl_xattr.h> 1162306a36Sopenharmony_ci#include <linux/xattr.h> 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include "debug.h" 1462306a36Sopenharmony_ci#include "ntfs.h" 1562306a36Sopenharmony_ci#include "ntfs_fs.h" 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci// clang-format off 1862306a36Sopenharmony_ci#define SYSTEM_DOS_ATTRIB "system.dos_attrib" 1962306a36Sopenharmony_ci#define SYSTEM_NTFS_ATTRIB "system.ntfs_attrib" 2062306a36Sopenharmony_ci#define SYSTEM_NTFS_ATTRIB_BE "system.ntfs_attrib_be" 2162306a36Sopenharmony_ci#define SYSTEM_NTFS_SECURITY "system.ntfs_security" 2262306a36Sopenharmony_ci// clang-format on 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_cistatic inline size_t unpacked_ea_size(const struct EA_FULL *ea) 2562306a36Sopenharmony_ci{ 2662306a36Sopenharmony_ci return ea->size ? le32_to_cpu(ea->size) : 2762306a36Sopenharmony_ci ALIGN(struct_size(ea, name, 2862306a36Sopenharmony_ci 1 + ea->name_len + 2962306a36Sopenharmony_ci le16_to_cpu(ea->elength)), 3062306a36Sopenharmony_ci 4); 3162306a36Sopenharmony_ci} 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistatic inline size_t packed_ea_size(const struct EA_FULL *ea) 3462306a36Sopenharmony_ci{ 3562306a36Sopenharmony_ci return struct_size(ea, name, 3662306a36Sopenharmony_ci 1 + ea->name_len + le16_to_cpu(ea->elength)) - 3762306a36Sopenharmony_ci offsetof(struct EA_FULL, flags); 3862306a36Sopenharmony_ci} 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci/* 4162306a36Sopenharmony_ci * find_ea 4262306a36Sopenharmony_ci * 4362306a36Sopenharmony_ci * Assume there is at least one xattr in the list. 4462306a36Sopenharmony_ci */ 4562306a36Sopenharmony_cistatic inline bool find_ea(const struct EA_FULL *ea_all, u32 bytes, 4662306a36Sopenharmony_ci const char *name, u8 name_len, u32 *off, u32 *ea_sz) 4762306a36Sopenharmony_ci{ 4862306a36Sopenharmony_ci u32 ea_size; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci *off = 0; 5162306a36Sopenharmony_ci if (!ea_all) 5262306a36Sopenharmony_ci return false; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci for (; *off < bytes; *off += ea_size) { 5562306a36Sopenharmony_ci const struct EA_FULL *ea = Add2Ptr(ea_all, *off); 5662306a36Sopenharmony_ci ea_size = unpacked_ea_size(ea); 5762306a36Sopenharmony_ci if (ea->name_len == name_len && 5862306a36Sopenharmony_ci !memcmp(ea->name, name, name_len)) { 5962306a36Sopenharmony_ci if (ea_sz) 6062306a36Sopenharmony_ci *ea_sz = ea_size; 6162306a36Sopenharmony_ci return true; 6262306a36Sopenharmony_ci } 6362306a36Sopenharmony_ci } 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci return false; 6662306a36Sopenharmony_ci} 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci/* 6962306a36Sopenharmony_ci * ntfs_read_ea - Read all extended attributes. 7062306a36Sopenharmony_ci * @ea: New allocated memory. 7162306a36Sopenharmony_ci * @info: Pointer into resident data. 7262306a36Sopenharmony_ci */ 7362306a36Sopenharmony_cistatic int ntfs_read_ea(struct ntfs_inode *ni, struct EA_FULL **ea, 7462306a36Sopenharmony_ci size_t add_bytes, const struct EA_INFO **info) 7562306a36Sopenharmony_ci{ 7662306a36Sopenharmony_ci int err = -EINVAL; 7762306a36Sopenharmony_ci struct ntfs_sb_info *sbi = ni->mi.sbi; 7862306a36Sopenharmony_ci struct ATTR_LIST_ENTRY *le = NULL; 7962306a36Sopenharmony_ci struct ATTRIB *attr_info, *attr_ea; 8062306a36Sopenharmony_ci void *ea_p; 8162306a36Sopenharmony_ci u32 size, off, ea_size; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci static_assert(le32_to_cpu(ATTR_EA_INFO) < le32_to_cpu(ATTR_EA)); 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci *ea = NULL; 8662306a36Sopenharmony_ci *info = NULL; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci attr_info = 8962306a36Sopenharmony_ci ni_find_attr(ni, NULL, &le, ATTR_EA_INFO, NULL, 0, NULL, NULL); 9062306a36Sopenharmony_ci attr_ea = 9162306a36Sopenharmony_ci ni_find_attr(ni, attr_info, &le, ATTR_EA, NULL, 0, NULL, NULL); 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci if (!attr_ea || !attr_info) 9462306a36Sopenharmony_ci return 0; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci *info = resident_data_ex(attr_info, sizeof(struct EA_INFO)); 9762306a36Sopenharmony_ci if (!*info) 9862306a36Sopenharmony_ci goto out; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci /* Check Ea limit. */ 10162306a36Sopenharmony_ci size = le32_to_cpu((*info)->size); 10262306a36Sopenharmony_ci if (size > sbi->ea_max_size) { 10362306a36Sopenharmony_ci err = -EFBIG; 10462306a36Sopenharmony_ci goto out; 10562306a36Sopenharmony_ci } 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci if (attr_size(attr_ea) > sbi->ea_max_size) { 10862306a36Sopenharmony_ci err = -EFBIG; 10962306a36Sopenharmony_ci goto out; 11062306a36Sopenharmony_ci } 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci if (!size) { 11362306a36Sopenharmony_ci /* EA info persists, but xattr is empty. Looks like EA problem. */ 11462306a36Sopenharmony_ci goto out; 11562306a36Sopenharmony_ci } 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci /* Allocate memory for packed Ea. */ 11862306a36Sopenharmony_ci ea_p = kmalloc(size_add(size, add_bytes), GFP_NOFS); 11962306a36Sopenharmony_ci if (!ea_p) 12062306a36Sopenharmony_ci return -ENOMEM; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci if (attr_ea->non_res) { 12362306a36Sopenharmony_ci struct runs_tree run; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci run_init(&run); 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci err = attr_load_runs_range(ni, ATTR_EA, NULL, 0, &run, 0, size); 12862306a36Sopenharmony_ci if (!err) 12962306a36Sopenharmony_ci err = ntfs_read_run_nb(sbi, &run, 0, ea_p, size, NULL); 13062306a36Sopenharmony_ci run_close(&run); 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci if (err) 13362306a36Sopenharmony_ci goto out1; 13462306a36Sopenharmony_ci } else { 13562306a36Sopenharmony_ci void *p = resident_data_ex(attr_ea, size); 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci if (!p) 13862306a36Sopenharmony_ci goto out1; 13962306a36Sopenharmony_ci memcpy(ea_p, p, size); 14062306a36Sopenharmony_ci } 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci memset(Add2Ptr(ea_p, size), 0, add_bytes); 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci err = -EINVAL; 14562306a36Sopenharmony_ci /* Check all attributes for consistency. */ 14662306a36Sopenharmony_ci for (off = 0; off < size; off += ea_size) { 14762306a36Sopenharmony_ci const struct EA_FULL *ef = Add2Ptr(ea_p, off); 14862306a36Sopenharmony_ci u32 bytes = size - off; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci /* Check if we can use field ea->size. */ 15162306a36Sopenharmony_ci if (bytes < sizeof(ef->size)) 15262306a36Sopenharmony_ci goto out1; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci if (ef->size) { 15562306a36Sopenharmony_ci ea_size = le32_to_cpu(ef->size); 15662306a36Sopenharmony_ci if (ea_size > bytes) 15762306a36Sopenharmony_ci goto out1; 15862306a36Sopenharmony_ci continue; 15962306a36Sopenharmony_ci } 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci /* Check if we can use fields ef->name_len and ef->elength. */ 16262306a36Sopenharmony_ci if (bytes < offsetof(struct EA_FULL, name)) 16362306a36Sopenharmony_ci goto out1; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci ea_size = ALIGN(struct_size(ef, name, 16662306a36Sopenharmony_ci 1 + ef->name_len + 16762306a36Sopenharmony_ci le16_to_cpu(ef->elength)), 16862306a36Sopenharmony_ci 4); 16962306a36Sopenharmony_ci if (ea_size > bytes) 17062306a36Sopenharmony_ci goto out1; 17162306a36Sopenharmony_ci } 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci *ea = ea_p; 17462306a36Sopenharmony_ci return 0; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ciout1: 17762306a36Sopenharmony_ci kfree(ea_p); 17862306a36Sopenharmony_ciout: 17962306a36Sopenharmony_ci ntfs_set_state(sbi, NTFS_DIRTY_DIRTY); 18062306a36Sopenharmony_ci return err; 18162306a36Sopenharmony_ci} 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci/* 18462306a36Sopenharmony_ci * ntfs_list_ea 18562306a36Sopenharmony_ci * 18662306a36Sopenharmony_ci * Copy a list of xattrs names into the buffer 18762306a36Sopenharmony_ci * provided, or compute the buffer size required. 18862306a36Sopenharmony_ci * 18962306a36Sopenharmony_ci * Return: 19062306a36Sopenharmony_ci * * Number of bytes used / required on 19162306a36Sopenharmony_ci * * -ERRNO - on failure 19262306a36Sopenharmony_ci */ 19362306a36Sopenharmony_cistatic ssize_t ntfs_list_ea(struct ntfs_inode *ni, char *buffer, 19462306a36Sopenharmony_ci size_t bytes_per_buffer) 19562306a36Sopenharmony_ci{ 19662306a36Sopenharmony_ci const struct EA_INFO *info; 19762306a36Sopenharmony_ci struct EA_FULL *ea_all = NULL; 19862306a36Sopenharmony_ci const struct EA_FULL *ea; 19962306a36Sopenharmony_ci u32 off, size; 20062306a36Sopenharmony_ci int err; 20162306a36Sopenharmony_ci int ea_size; 20262306a36Sopenharmony_ci size_t ret; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci err = ntfs_read_ea(ni, &ea_all, 0, &info); 20562306a36Sopenharmony_ci if (err) 20662306a36Sopenharmony_ci return err; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci if (!info || !ea_all) 20962306a36Sopenharmony_ci return 0; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci size = le32_to_cpu(info->size); 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci /* Enumerate all xattrs. */ 21462306a36Sopenharmony_ci ret = 0; 21562306a36Sopenharmony_ci for (off = 0; off + sizeof(struct EA_FULL) < size; off += ea_size) { 21662306a36Sopenharmony_ci ea = Add2Ptr(ea_all, off); 21762306a36Sopenharmony_ci ea_size = unpacked_ea_size(ea); 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci if (!ea->name_len) 22062306a36Sopenharmony_ci break; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci if (ea->name_len > ea_size) 22362306a36Sopenharmony_ci break; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci if (buffer) { 22662306a36Sopenharmony_ci /* Check if we can use field ea->name */ 22762306a36Sopenharmony_ci if (off + ea_size > size) 22862306a36Sopenharmony_ci break; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci if (ret + ea->name_len + 1 > bytes_per_buffer) { 23162306a36Sopenharmony_ci err = -ERANGE; 23262306a36Sopenharmony_ci goto out; 23362306a36Sopenharmony_ci } 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci memcpy(buffer + ret, ea->name, ea->name_len); 23662306a36Sopenharmony_ci buffer[ret + ea->name_len] = 0; 23762306a36Sopenharmony_ci } 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci ret += ea->name_len + 1; 24062306a36Sopenharmony_ci } 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ciout: 24362306a36Sopenharmony_ci kfree(ea_all); 24462306a36Sopenharmony_ci return err ? err : ret; 24562306a36Sopenharmony_ci} 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_cistatic int ntfs_get_ea(struct inode *inode, const char *name, size_t name_len, 24862306a36Sopenharmony_ci void *buffer, size_t size, size_t *required) 24962306a36Sopenharmony_ci{ 25062306a36Sopenharmony_ci struct ntfs_inode *ni = ntfs_i(inode); 25162306a36Sopenharmony_ci const struct EA_INFO *info; 25262306a36Sopenharmony_ci struct EA_FULL *ea_all = NULL; 25362306a36Sopenharmony_ci const struct EA_FULL *ea; 25462306a36Sopenharmony_ci u32 off, len; 25562306a36Sopenharmony_ci int err; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci if (!(ni->ni_flags & NI_FLAG_EA)) 25862306a36Sopenharmony_ci return -ENODATA; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci if (!required) 26162306a36Sopenharmony_ci ni_lock(ni); 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci len = 0; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci if (name_len > 255) { 26662306a36Sopenharmony_ci err = -ENAMETOOLONG; 26762306a36Sopenharmony_ci goto out; 26862306a36Sopenharmony_ci } 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci err = ntfs_read_ea(ni, &ea_all, 0, &info); 27162306a36Sopenharmony_ci if (err) 27262306a36Sopenharmony_ci goto out; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci if (!info) 27562306a36Sopenharmony_ci goto out; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci /* Enumerate all xattrs. */ 27862306a36Sopenharmony_ci if (!find_ea(ea_all, le32_to_cpu(info->size), name, name_len, &off, 27962306a36Sopenharmony_ci NULL)) { 28062306a36Sopenharmony_ci err = -ENODATA; 28162306a36Sopenharmony_ci goto out; 28262306a36Sopenharmony_ci } 28362306a36Sopenharmony_ci ea = Add2Ptr(ea_all, off); 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci len = le16_to_cpu(ea->elength); 28662306a36Sopenharmony_ci if (!buffer) { 28762306a36Sopenharmony_ci err = 0; 28862306a36Sopenharmony_ci goto out; 28962306a36Sopenharmony_ci } 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci if (len > size) { 29262306a36Sopenharmony_ci err = -ERANGE; 29362306a36Sopenharmony_ci if (required) 29462306a36Sopenharmony_ci *required = len; 29562306a36Sopenharmony_ci goto out; 29662306a36Sopenharmony_ci } 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci memcpy(buffer, ea->name + ea->name_len + 1, len); 29962306a36Sopenharmony_ci err = 0; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ciout: 30262306a36Sopenharmony_ci kfree(ea_all); 30362306a36Sopenharmony_ci if (!required) 30462306a36Sopenharmony_ci ni_unlock(ni); 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci return err ? err : len; 30762306a36Sopenharmony_ci} 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_cistatic noinline int ntfs_set_ea(struct inode *inode, const char *name, 31062306a36Sopenharmony_ci size_t name_len, const void *value, 31162306a36Sopenharmony_ci size_t val_size, int flags, bool locked, 31262306a36Sopenharmony_ci __le16 *ea_size) 31362306a36Sopenharmony_ci{ 31462306a36Sopenharmony_ci struct ntfs_inode *ni = ntfs_i(inode); 31562306a36Sopenharmony_ci struct ntfs_sb_info *sbi = ni->mi.sbi; 31662306a36Sopenharmony_ci int err; 31762306a36Sopenharmony_ci struct EA_INFO ea_info; 31862306a36Sopenharmony_ci const struct EA_INFO *info; 31962306a36Sopenharmony_ci struct EA_FULL *new_ea; 32062306a36Sopenharmony_ci struct EA_FULL *ea_all = NULL; 32162306a36Sopenharmony_ci size_t add, new_pack; 32262306a36Sopenharmony_ci u32 off, size, ea_sz; 32362306a36Sopenharmony_ci __le16 size_pack; 32462306a36Sopenharmony_ci struct ATTRIB *attr; 32562306a36Sopenharmony_ci struct ATTR_LIST_ENTRY *le; 32662306a36Sopenharmony_ci struct mft_inode *mi; 32762306a36Sopenharmony_ci struct runs_tree ea_run; 32862306a36Sopenharmony_ci u64 new_sz; 32962306a36Sopenharmony_ci void *p; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci if (!locked) 33262306a36Sopenharmony_ci ni_lock(ni); 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci run_init(&ea_run); 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci if (name_len > 255) { 33762306a36Sopenharmony_ci err = -ENAMETOOLONG; 33862306a36Sopenharmony_ci goto out; 33962306a36Sopenharmony_ci } 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci add = ALIGN(struct_size(ea_all, name, 1 + name_len + val_size), 4); 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci err = ntfs_read_ea(ni, &ea_all, add, &info); 34462306a36Sopenharmony_ci if (err) 34562306a36Sopenharmony_ci goto out; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci if (!info) { 34862306a36Sopenharmony_ci memset(&ea_info, 0, sizeof(ea_info)); 34962306a36Sopenharmony_ci size = 0; 35062306a36Sopenharmony_ci size_pack = 0; 35162306a36Sopenharmony_ci } else { 35262306a36Sopenharmony_ci memcpy(&ea_info, info, sizeof(ea_info)); 35362306a36Sopenharmony_ci size = le32_to_cpu(ea_info.size); 35462306a36Sopenharmony_ci size_pack = ea_info.size_pack; 35562306a36Sopenharmony_ci } 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci if (info && find_ea(ea_all, size, name, name_len, &off, &ea_sz)) { 35862306a36Sopenharmony_ci struct EA_FULL *ea; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci if (flags & XATTR_CREATE) { 36162306a36Sopenharmony_ci err = -EEXIST; 36262306a36Sopenharmony_ci goto out; 36362306a36Sopenharmony_ci } 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci ea = Add2Ptr(ea_all, off); 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci /* 36862306a36Sopenharmony_ci * Check simple case when we try to insert xattr with the same value 36962306a36Sopenharmony_ci * e.g. ntfs_save_wsl_perm 37062306a36Sopenharmony_ci */ 37162306a36Sopenharmony_ci if (val_size && le16_to_cpu(ea->elength) == val_size && 37262306a36Sopenharmony_ci !memcmp(ea->name + ea->name_len + 1, value, val_size)) { 37362306a36Sopenharmony_ci /* xattr already contains the required value. */ 37462306a36Sopenharmony_ci goto out; 37562306a36Sopenharmony_ci } 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci /* Remove current xattr. */ 37862306a36Sopenharmony_ci if (ea->flags & FILE_NEED_EA) 37962306a36Sopenharmony_ci le16_add_cpu(&ea_info.count, -1); 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci le16_add_cpu(&ea_info.size_pack, 0 - packed_ea_size(ea)); 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci memmove(ea, Add2Ptr(ea, ea_sz), size - off - ea_sz); 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci size -= ea_sz; 38662306a36Sopenharmony_ci memset(Add2Ptr(ea_all, size), 0, ea_sz); 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci ea_info.size = cpu_to_le32(size); 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci if ((flags & XATTR_REPLACE) && !val_size) { 39162306a36Sopenharmony_ci /* Remove xattr. */ 39262306a36Sopenharmony_ci goto update_ea; 39362306a36Sopenharmony_ci } 39462306a36Sopenharmony_ci } else { 39562306a36Sopenharmony_ci if (flags & XATTR_REPLACE) { 39662306a36Sopenharmony_ci err = -ENODATA; 39762306a36Sopenharmony_ci goto out; 39862306a36Sopenharmony_ci } 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci if (!ea_all) { 40162306a36Sopenharmony_ci ea_all = kzalloc(add, GFP_NOFS); 40262306a36Sopenharmony_ci if (!ea_all) { 40362306a36Sopenharmony_ci err = -ENOMEM; 40462306a36Sopenharmony_ci goto out; 40562306a36Sopenharmony_ci } 40662306a36Sopenharmony_ci } 40762306a36Sopenharmony_ci } 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci /* Append new xattr. */ 41062306a36Sopenharmony_ci new_ea = Add2Ptr(ea_all, size); 41162306a36Sopenharmony_ci new_ea->size = cpu_to_le32(add); 41262306a36Sopenharmony_ci new_ea->flags = 0; 41362306a36Sopenharmony_ci new_ea->name_len = name_len; 41462306a36Sopenharmony_ci new_ea->elength = cpu_to_le16(val_size); 41562306a36Sopenharmony_ci memcpy(new_ea->name, name, name_len); 41662306a36Sopenharmony_ci new_ea->name[name_len] = 0; 41762306a36Sopenharmony_ci memcpy(new_ea->name + name_len + 1, value, val_size); 41862306a36Sopenharmony_ci new_pack = le16_to_cpu(ea_info.size_pack) + packed_ea_size(new_ea); 41962306a36Sopenharmony_ci ea_info.size_pack = cpu_to_le16(new_pack); 42062306a36Sopenharmony_ci /* New size of ATTR_EA. */ 42162306a36Sopenharmony_ci size += add; 42262306a36Sopenharmony_ci ea_info.size = cpu_to_le32(size); 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci /* 42562306a36Sopenharmony_ci * 1. Check ea_info.size_pack for overflow. 42662306a36Sopenharmony_ci * 2. New attribute size must fit value from $AttrDef 42762306a36Sopenharmony_ci */ 42862306a36Sopenharmony_ci if (new_pack > 0xffff || size > sbi->ea_max_size) { 42962306a36Sopenharmony_ci ntfs_inode_warn( 43062306a36Sopenharmony_ci inode, 43162306a36Sopenharmony_ci "The size of extended attributes must not exceed 64KiB"); 43262306a36Sopenharmony_ci err = -EFBIG; // -EINVAL? 43362306a36Sopenharmony_ci goto out; 43462306a36Sopenharmony_ci } 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ciupdate_ea: 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci if (!info) { 43962306a36Sopenharmony_ci /* Create xattr. */ 44062306a36Sopenharmony_ci if (!size) { 44162306a36Sopenharmony_ci err = 0; 44262306a36Sopenharmony_ci goto out; 44362306a36Sopenharmony_ci } 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci err = ni_insert_resident(ni, sizeof(struct EA_INFO), 44662306a36Sopenharmony_ci ATTR_EA_INFO, NULL, 0, NULL, NULL, 44762306a36Sopenharmony_ci NULL); 44862306a36Sopenharmony_ci if (err) 44962306a36Sopenharmony_ci goto out; 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci err = ni_insert_resident(ni, 0, ATTR_EA, NULL, 0, NULL, NULL, 45262306a36Sopenharmony_ci NULL); 45362306a36Sopenharmony_ci if (err) 45462306a36Sopenharmony_ci goto out; 45562306a36Sopenharmony_ci } 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci new_sz = size; 45862306a36Sopenharmony_ci err = attr_set_size(ni, ATTR_EA, NULL, 0, &ea_run, new_sz, &new_sz, 45962306a36Sopenharmony_ci false, NULL); 46062306a36Sopenharmony_ci if (err) 46162306a36Sopenharmony_ci goto out; 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci le = NULL; 46462306a36Sopenharmony_ci attr = ni_find_attr(ni, NULL, &le, ATTR_EA_INFO, NULL, 0, NULL, &mi); 46562306a36Sopenharmony_ci if (!attr) { 46662306a36Sopenharmony_ci err = -EINVAL; 46762306a36Sopenharmony_ci goto out; 46862306a36Sopenharmony_ci } 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci if (!size) { 47162306a36Sopenharmony_ci /* Delete xattr, ATTR_EA_INFO */ 47262306a36Sopenharmony_ci ni_remove_attr_le(ni, attr, mi, le); 47362306a36Sopenharmony_ci } else { 47462306a36Sopenharmony_ci p = resident_data_ex(attr, sizeof(struct EA_INFO)); 47562306a36Sopenharmony_ci if (!p) { 47662306a36Sopenharmony_ci err = -EINVAL; 47762306a36Sopenharmony_ci goto out; 47862306a36Sopenharmony_ci } 47962306a36Sopenharmony_ci memcpy(p, &ea_info, sizeof(struct EA_INFO)); 48062306a36Sopenharmony_ci mi->dirty = true; 48162306a36Sopenharmony_ci } 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci le = NULL; 48462306a36Sopenharmony_ci attr = ni_find_attr(ni, NULL, &le, ATTR_EA, NULL, 0, NULL, &mi); 48562306a36Sopenharmony_ci if (!attr) { 48662306a36Sopenharmony_ci err = -EINVAL; 48762306a36Sopenharmony_ci goto out; 48862306a36Sopenharmony_ci } 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci if (!size) { 49162306a36Sopenharmony_ci /* Delete xattr, ATTR_EA */ 49262306a36Sopenharmony_ci ni_remove_attr_le(ni, attr, mi, le); 49362306a36Sopenharmony_ci } else if (attr->non_res) { 49462306a36Sopenharmony_ci err = attr_load_runs_range(ni, ATTR_EA, NULL, 0, &ea_run, 0, 49562306a36Sopenharmony_ci size); 49662306a36Sopenharmony_ci if (err) 49762306a36Sopenharmony_ci goto out; 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci err = ntfs_sb_write_run(sbi, &ea_run, 0, ea_all, size, 0); 50062306a36Sopenharmony_ci if (err) 50162306a36Sopenharmony_ci goto out; 50262306a36Sopenharmony_ci } else { 50362306a36Sopenharmony_ci p = resident_data_ex(attr, size); 50462306a36Sopenharmony_ci if (!p) { 50562306a36Sopenharmony_ci err = -EINVAL; 50662306a36Sopenharmony_ci goto out; 50762306a36Sopenharmony_ci } 50862306a36Sopenharmony_ci memcpy(p, ea_all, size); 50962306a36Sopenharmony_ci mi->dirty = true; 51062306a36Sopenharmony_ci } 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci /* Check if we delete the last xattr. */ 51362306a36Sopenharmony_ci if (size) 51462306a36Sopenharmony_ci ni->ni_flags |= NI_FLAG_EA; 51562306a36Sopenharmony_ci else 51662306a36Sopenharmony_ci ni->ni_flags &= ~NI_FLAG_EA; 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci if (ea_info.size_pack != size_pack) 51962306a36Sopenharmony_ci ni->ni_flags |= NI_FLAG_UPDATE_PARENT; 52062306a36Sopenharmony_ci if (ea_size) 52162306a36Sopenharmony_ci *ea_size = ea_info.size_pack; 52262306a36Sopenharmony_ci mark_inode_dirty(&ni->vfs_inode); 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ciout: 52562306a36Sopenharmony_ci if (!locked) 52662306a36Sopenharmony_ci ni_unlock(ni); 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci run_close(&ea_run); 52962306a36Sopenharmony_ci kfree(ea_all); 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci return err; 53262306a36Sopenharmony_ci} 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci#ifdef CONFIG_NTFS3_FS_POSIX_ACL 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci/* 53762306a36Sopenharmony_ci * ntfs_get_acl - inode_operations::get_acl 53862306a36Sopenharmony_ci */ 53962306a36Sopenharmony_cistruct posix_acl *ntfs_get_acl(struct mnt_idmap *idmap, struct dentry *dentry, 54062306a36Sopenharmony_ci int type) 54162306a36Sopenharmony_ci{ 54262306a36Sopenharmony_ci struct inode *inode = d_inode(dentry); 54362306a36Sopenharmony_ci struct ntfs_inode *ni = ntfs_i(inode); 54462306a36Sopenharmony_ci const char *name; 54562306a36Sopenharmony_ci size_t name_len; 54662306a36Sopenharmony_ci struct posix_acl *acl; 54762306a36Sopenharmony_ci size_t req; 54862306a36Sopenharmony_ci int err; 54962306a36Sopenharmony_ci void *buf; 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci /* Allocate PATH_MAX bytes. */ 55262306a36Sopenharmony_ci buf = __getname(); 55362306a36Sopenharmony_ci if (!buf) 55462306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci /* Possible values of 'type' was already checked above. */ 55762306a36Sopenharmony_ci if (type == ACL_TYPE_ACCESS) { 55862306a36Sopenharmony_ci name = XATTR_NAME_POSIX_ACL_ACCESS; 55962306a36Sopenharmony_ci name_len = sizeof(XATTR_NAME_POSIX_ACL_ACCESS) - 1; 56062306a36Sopenharmony_ci } else { 56162306a36Sopenharmony_ci name = XATTR_NAME_POSIX_ACL_DEFAULT; 56262306a36Sopenharmony_ci name_len = sizeof(XATTR_NAME_POSIX_ACL_DEFAULT) - 1; 56362306a36Sopenharmony_ci } 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci ni_lock(ni); 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci err = ntfs_get_ea(inode, name, name_len, buf, PATH_MAX, &req); 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci ni_unlock(ni); 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci /* Translate extended attribute to acl. */ 57262306a36Sopenharmony_ci if (err >= 0) { 57362306a36Sopenharmony_ci acl = posix_acl_from_xattr(&init_user_ns, buf, err); 57462306a36Sopenharmony_ci } else if (err == -ENODATA) { 57562306a36Sopenharmony_ci acl = NULL; 57662306a36Sopenharmony_ci } else { 57762306a36Sopenharmony_ci acl = ERR_PTR(err); 57862306a36Sopenharmony_ci } 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci if (!IS_ERR(acl)) 58162306a36Sopenharmony_ci set_cached_acl(inode, type, acl); 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci __putname(buf); 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci return acl; 58662306a36Sopenharmony_ci} 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_cistatic noinline int ntfs_set_acl_ex(struct mnt_idmap *idmap, 58962306a36Sopenharmony_ci struct inode *inode, struct posix_acl *acl, 59062306a36Sopenharmony_ci int type, bool init_acl) 59162306a36Sopenharmony_ci{ 59262306a36Sopenharmony_ci const char *name; 59362306a36Sopenharmony_ci size_t size, name_len; 59462306a36Sopenharmony_ci void *value; 59562306a36Sopenharmony_ci int err; 59662306a36Sopenharmony_ci int flags; 59762306a36Sopenharmony_ci umode_t mode; 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci if (S_ISLNK(inode->i_mode)) 60062306a36Sopenharmony_ci return -EOPNOTSUPP; 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci mode = inode->i_mode; 60362306a36Sopenharmony_ci switch (type) { 60462306a36Sopenharmony_ci case ACL_TYPE_ACCESS: 60562306a36Sopenharmony_ci /* Do not change i_mode if we are in init_acl */ 60662306a36Sopenharmony_ci if (acl && !init_acl) { 60762306a36Sopenharmony_ci err = posix_acl_update_mode(idmap, inode, &mode, &acl); 60862306a36Sopenharmony_ci if (err) 60962306a36Sopenharmony_ci return err; 61062306a36Sopenharmony_ci } 61162306a36Sopenharmony_ci name = XATTR_NAME_POSIX_ACL_ACCESS; 61262306a36Sopenharmony_ci name_len = sizeof(XATTR_NAME_POSIX_ACL_ACCESS) - 1; 61362306a36Sopenharmony_ci break; 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci case ACL_TYPE_DEFAULT: 61662306a36Sopenharmony_ci if (!S_ISDIR(inode->i_mode)) 61762306a36Sopenharmony_ci return acl ? -EACCES : 0; 61862306a36Sopenharmony_ci name = XATTR_NAME_POSIX_ACL_DEFAULT; 61962306a36Sopenharmony_ci name_len = sizeof(XATTR_NAME_POSIX_ACL_DEFAULT) - 1; 62062306a36Sopenharmony_ci break; 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci default: 62362306a36Sopenharmony_ci return -EINVAL; 62462306a36Sopenharmony_ci } 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci if (!acl) { 62762306a36Sopenharmony_ci /* Remove xattr if it can be presented via mode. */ 62862306a36Sopenharmony_ci size = 0; 62962306a36Sopenharmony_ci value = NULL; 63062306a36Sopenharmony_ci flags = XATTR_REPLACE; 63162306a36Sopenharmony_ci } else { 63262306a36Sopenharmony_ci size = posix_acl_xattr_size(acl->a_count); 63362306a36Sopenharmony_ci value = kmalloc(size, GFP_NOFS); 63462306a36Sopenharmony_ci if (!value) 63562306a36Sopenharmony_ci return -ENOMEM; 63662306a36Sopenharmony_ci err = posix_acl_to_xattr(&init_user_ns, acl, value, size); 63762306a36Sopenharmony_ci if (err < 0) 63862306a36Sopenharmony_ci goto out; 63962306a36Sopenharmony_ci flags = 0; 64062306a36Sopenharmony_ci } 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci err = ntfs_set_ea(inode, name, name_len, value, size, flags, 0, NULL); 64362306a36Sopenharmony_ci if (err == -ENODATA && !size) 64462306a36Sopenharmony_ci err = 0; /* Removing non existed xattr. */ 64562306a36Sopenharmony_ci if (!err) { 64662306a36Sopenharmony_ci set_cached_acl(inode, type, acl); 64762306a36Sopenharmony_ci inode->i_mode = mode; 64862306a36Sopenharmony_ci inode_set_ctime_current(inode); 64962306a36Sopenharmony_ci mark_inode_dirty(inode); 65062306a36Sopenharmony_ci } 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ciout: 65362306a36Sopenharmony_ci kfree(value); 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci return err; 65662306a36Sopenharmony_ci} 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci/* 65962306a36Sopenharmony_ci * ntfs_set_acl - inode_operations::set_acl 66062306a36Sopenharmony_ci */ 66162306a36Sopenharmony_ciint ntfs_set_acl(struct mnt_idmap *idmap, struct dentry *dentry, 66262306a36Sopenharmony_ci struct posix_acl *acl, int type) 66362306a36Sopenharmony_ci{ 66462306a36Sopenharmony_ci return ntfs_set_acl_ex(idmap, d_inode(dentry), acl, type, false); 66562306a36Sopenharmony_ci} 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci/* 66862306a36Sopenharmony_ci * ntfs_init_acl - Initialize the ACLs of a new inode. 66962306a36Sopenharmony_ci * 67062306a36Sopenharmony_ci * Called from ntfs_create_inode(). 67162306a36Sopenharmony_ci */ 67262306a36Sopenharmony_ciint ntfs_init_acl(struct mnt_idmap *idmap, struct inode *inode, 67362306a36Sopenharmony_ci struct inode *dir) 67462306a36Sopenharmony_ci{ 67562306a36Sopenharmony_ci struct posix_acl *default_acl, *acl; 67662306a36Sopenharmony_ci int err; 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci err = posix_acl_create(dir, &inode->i_mode, &default_acl, &acl); 67962306a36Sopenharmony_ci if (err) 68062306a36Sopenharmony_ci return err; 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci if (default_acl) { 68362306a36Sopenharmony_ci err = ntfs_set_acl_ex(idmap, inode, default_acl, 68462306a36Sopenharmony_ci ACL_TYPE_DEFAULT, true); 68562306a36Sopenharmony_ci posix_acl_release(default_acl); 68662306a36Sopenharmony_ci } else { 68762306a36Sopenharmony_ci inode->i_default_acl = NULL; 68862306a36Sopenharmony_ci } 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci if (acl) { 69162306a36Sopenharmony_ci if (!err) 69262306a36Sopenharmony_ci err = ntfs_set_acl_ex(idmap, inode, acl, 69362306a36Sopenharmony_ci ACL_TYPE_ACCESS, true); 69462306a36Sopenharmony_ci posix_acl_release(acl); 69562306a36Sopenharmony_ci } else { 69662306a36Sopenharmony_ci inode->i_acl = NULL; 69762306a36Sopenharmony_ci } 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci return err; 70062306a36Sopenharmony_ci} 70162306a36Sopenharmony_ci#endif 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci/* 70462306a36Sopenharmony_ci * ntfs_acl_chmod - Helper for ntfs3_setattr(). 70562306a36Sopenharmony_ci */ 70662306a36Sopenharmony_ciint ntfs_acl_chmod(struct mnt_idmap *idmap, struct dentry *dentry) 70762306a36Sopenharmony_ci{ 70862306a36Sopenharmony_ci struct inode *inode = d_inode(dentry); 70962306a36Sopenharmony_ci struct super_block *sb = inode->i_sb; 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci if (!(sb->s_flags & SB_POSIXACL)) 71262306a36Sopenharmony_ci return 0; 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci if (S_ISLNK(inode->i_mode)) 71562306a36Sopenharmony_ci return -EOPNOTSUPP; 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci return posix_acl_chmod(idmap, dentry, inode->i_mode); 71862306a36Sopenharmony_ci} 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci/* 72162306a36Sopenharmony_ci * ntfs_listxattr - inode_operations::listxattr 72262306a36Sopenharmony_ci */ 72362306a36Sopenharmony_cissize_t ntfs_listxattr(struct dentry *dentry, char *buffer, size_t size) 72462306a36Sopenharmony_ci{ 72562306a36Sopenharmony_ci struct inode *inode = d_inode(dentry); 72662306a36Sopenharmony_ci struct ntfs_inode *ni = ntfs_i(inode); 72762306a36Sopenharmony_ci ssize_t ret; 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci if (!(ni->ni_flags & NI_FLAG_EA)) { 73062306a36Sopenharmony_ci /* no xattr in file */ 73162306a36Sopenharmony_ci return 0; 73262306a36Sopenharmony_ci } 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci ni_lock(ni); 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci ret = ntfs_list_ea(ni, buffer, size); 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci ni_unlock(ni); 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci return ret; 74162306a36Sopenharmony_ci} 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_cistatic int ntfs_getxattr(const struct xattr_handler *handler, struct dentry *de, 74462306a36Sopenharmony_ci struct inode *inode, const char *name, void *buffer, 74562306a36Sopenharmony_ci size_t size) 74662306a36Sopenharmony_ci{ 74762306a36Sopenharmony_ci int err; 74862306a36Sopenharmony_ci struct ntfs_inode *ni = ntfs_i(inode); 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci if (unlikely(ntfs3_forced_shutdown(inode->i_sb))) 75162306a36Sopenharmony_ci return -EIO; 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci /* Dispatch request. */ 75462306a36Sopenharmony_ci if (!strcmp(name, SYSTEM_DOS_ATTRIB)) { 75562306a36Sopenharmony_ci /* system.dos_attrib */ 75662306a36Sopenharmony_ci if (!buffer) { 75762306a36Sopenharmony_ci err = sizeof(u8); 75862306a36Sopenharmony_ci } else if (size < sizeof(u8)) { 75962306a36Sopenharmony_ci err = -ENODATA; 76062306a36Sopenharmony_ci } else { 76162306a36Sopenharmony_ci err = sizeof(u8); 76262306a36Sopenharmony_ci *(u8 *)buffer = le32_to_cpu(ni->std_fa); 76362306a36Sopenharmony_ci } 76462306a36Sopenharmony_ci goto out; 76562306a36Sopenharmony_ci } 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci if (!strcmp(name, SYSTEM_NTFS_ATTRIB) || 76862306a36Sopenharmony_ci !strcmp(name, SYSTEM_NTFS_ATTRIB_BE)) { 76962306a36Sopenharmony_ci /* system.ntfs_attrib */ 77062306a36Sopenharmony_ci if (!buffer) { 77162306a36Sopenharmony_ci err = sizeof(u32); 77262306a36Sopenharmony_ci } else if (size < sizeof(u32)) { 77362306a36Sopenharmony_ci err = -ENODATA; 77462306a36Sopenharmony_ci } else { 77562306a36Sopenharmony_ci err = sizeof(u32); 77662306a36Sopenharmony_ci *(u32 *)buffer = le32_to_cpu(ni->std_fa); 77762306a36Sopenharmony_ci if (!strcmp(name, SYSTEM_NTFS_ATTRIB_BE)) 77862306a36Sopenharmony_ci *(__be32 *)buffer = cpu_to_be32(*(u32 *)buffer); 77962306a36Sopenharmony_ci } 78062306a36Sopenharmony_ci goto out; 78162306a36Sopenharmony_ci } 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci if (!strcmp(name, SYSTEM_NTFS_SECURITY)) { 78462306a36Sopenharmony_ci /* system.ntfs_security*/ 78562306a36Sopenharmony_ci struct SECURITY_DESCRIPTOR_RELATIVE *sd = NULL; 78662306a36Sopenharmony_ci size_t sd_size = 0; 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci if (!is_ntfs3(ni->mi.sbi)) { 78962306a36Sopenharmony_ci /* We should get nt4 security. */ 79062306a36Sopenharmony_ci err = -EINVAL; 79162306a36Sopenharmony_ci goto out; 79262306a36Sopenharmony_ci } else if (le32_to_cpu(ni->std_security_id) < 79362306a36Sopenharmony_ci SECURITY_ID_FIRST) { 79462306a36Sopenharmony_ci err = -ENOENT; 79562306a36Sopenharmony_ci goto out; 79662306a36Sopenharmony_ci } 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ci err = ntfs_get_security_by_id(ni->mi.sbi, ni->std_security_id, 79962306a36Sopenharmony_ci &sd, &sd_size); 80062306a36Sopenharmony_ci if (err) 80162306a36Sopenharmony_ci goto out; 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_ci if (!is_sd_valid(sd, sd_size)) { 80462306a36Sopenharmony_ci ntfs_inode_warn( 80562306a36Sopenharmony_ci inode, 80662306a36Sopenharmony_ci "looks like you get incorrect security descriptor id=%u", 80762306a36Sopenharmony_ci ni->std_security_id); 80862306a36Sopenharmony_ci } 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci if (!buffer) { 81162306a36Sopenharmony_ci err = sd_size; 81262306a36Sopenharmony_ci } else if (size < sd_size) { 81362306a36Sopenharmony_ci err = -ENODATA; 81462306a36Sopenharmony_ci } else { 81562306a36Sopenharmony_ci err = sd_size; 81662306a36Sopenharmony_ci memcpy(buffer, sd, sd_size); 81762306a36Sopenharmony_ci } 81862306a36Sopenharmony_ci kfree(sd); 81962306a36Sopenharmony_ci goto out; 82062306a36Sopenharmony_ci } 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci /* Deal with NTFS extended attribute. */ 82362306a36Sopenharmony_ci err = ntfs_get_ea(inode, name, strlen(name), buffer, size, NULL); 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_ciout: 82662306a36Sopenharmony_ci return err; 82762306a36Sopenharmony_ci} 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_ci/* 83062306a36Sopenharmony_ci * ntfs_setxattr - inode_operations::setxattr 83162306a36Sopenharmony_ci */ 83262306a36Sopenharmony_cistatic noinline int ntfs_setxattr(const struct xattr_handler *handler, 83362306a36Sopenharmony_ci struct mnt_idmap *idmap, struct dentry *de, 83462306a36Sopenharmony_ci struct inode *inode, const char *name, 83562306a36Sopenharmony_ci const void *value, size_t size, int flags) 83662306a36Sopenharmony_ci{ 83762306a36Sopenharmony_ci int err = -EINVAL; 83862306a36Sopenharmony_ci struct ntfs_inode *ni = ntfs_i(inode); 83962306a36Sopenharmony_ci enum FILE_ATTRIBUTE new_fa; 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci /* Dispatch request. */ 84262306a36Sopenharmony_ci if (!strcmp(name, SYSTEM_DOS_ATTRIB)) { 84362306a36Sopenharmony_ci if (sizeof(u8) != size) 84462306a36Sopenharmony_ci goto out; 84562306a36Sopenharmony_ci new_fa = cpu_to_le32(*(u8 *)value); 84662306a36Sopenharmony_ci goto set_new_fa; 84762306a36Sopenharmony_ci } 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci if (!strcmp(name, SYSTEM_NTFS_ATTRIB) || 85062306a36Sopenharmony_ci !strcmp(name, SYSTEM_NTFS_ATTRIB_BE)) { 85162306a36Sopenharmony_ci if (size != sizeof(u32)) 85262306a36Sopenharmony_ci goto out; 85362306a36Sopenharmony_ci if (!strcmp(name, SYSTEM_NTFS_ATTRIB_BE)) 85462306a36Sopenharmony_ci new_fa = cpu_to_le32(be32_to_cpu(*(__be32 *)value)); 85562306a36Sopenharmony_ci else 85662306a36Sopenharmony_ci new_fa = cpu_to_le32(*(u32 *)value); 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_ci if (S_ISREG(inode->i_mode)) { 85962306a36Sopenharmony_ci /* Process compressed/sparsed in special way. */ 86062306a36Sopenharmony_ci ni_lock(ni); 86162306a36Sopenharmony_ci err = ni_new_attr_flags(ni, new_fa); 86262306a36Sopenharmony_ci ni_unlock(ni); 86362306a36Sopenharmony_ci if (err) 86462306a36Sopenharmony_ci goto out; 86562306a36Sopenharmony_ci } 86662306a36Sopenharmony_ciset_new_fa: 86762306a36Sopenharmony_ci /* 86862306a36Sopenharmony_ci * Thanks Mark Harmstone: 86962306a36Sopenharmony_ci * Keep directory bit consistency. 87062306a36Sopenharmony_ci */ 87162306a36Sopenharmony_ci if (S_ISDIR(inode->i_mode)) 87262306a36Sopenharmony_ci new_fa |= FILE_ATTRIBUTE_DIRECTORY; 87362306a36Sopenharmony_ci else 87462306a36Sopenharmony_ci new_fa &= ~FILE_ATTRIBUTE_DIRECTORY; 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_ci if (ni->std_fa != new_fa) { 87762306a36Sopenharmony_ci ni->std_fa = new_fa; 87862306a36Sopenharmony_ci if (new_fa & FILE_ATTRIBUTE_READONLY) 87962306a36Sopenharmony_ci inode->i_mode &= ~0222; 88062306a36Sopenharmony_ci else 88162306a36Sopenharmony_ci inode->i_mode |= 0222; 88262306a36Sopenharmony_ci /* Std attribute always in primary record. */ 88362306a36Sopenharmony_ci ni->mi.dirty = true; 88462306a36Sopenharmony_ci mark_inode_dirty(inode); 88562306a36Sopenharmony_ci } 88662306a36Sopenharmony_ci err = 0; 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_ci goto out; 88962306a36Sopenharmony_ci } 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci if (!strcmp(name, SYSTEM_NTFS_SECURITY)) { 89262306a36Sopenharmony_ci /* system.ntfs_security*/ 89362306a36Sopenharmony_ci __le32 security_id; 89462306a36Sopenharmony_ci bool inserted; 89562306a36Sopenharmony_ci struct ATTR_STD_INFO5 *std; 89662306a36Sopenharmony_ci 89762306a36Sopenharmony_ci if (!is_ntfs3(ni->mi.sbi)) { 89862306a36Sopenharmony_ci /* 89962306a36Sopenharmony_ci * We should replace ATTR_SECURE. 90062306a36Sopenharmony_ci * Skip this way cause it is nt4 feature. 90162306a36Sopenharmony_ci */ 90262306a36Sopenharmony_ci err = -EINVAL; 90362306a36Sopenharmony_ci goto out; 90462306a36Sopenharmony_ci } 90562306a36Sopenharmony_ci 90662306a36Sopenharmony_ci if (!is_sd_valid(value, size)) { 90762306a36Sopenharmony_ci err = -EINVAL; 90862306a36Sopenharmony_ci ntfs_inode_warn( 90962306a36Sopenharmony_ci inode, 91062306a36Sopenharmony_ci "you try to set invalid security descriptor"); 91162306a36Sopenharmony_ci goto out; 91262306a36Sopenharmony_ci } 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_ci err = ntfs_insert_security(ni->mi.sbi, value, size, 91562306a36Sopenharmony_ci &security_id, &inserted); 91662306a36Sopenharmony_ci if (err) 91762306a36Sopenharmony_ci goto out; 91862306a36Sopenharmony_ci 91962306a36Sopenharmony_ci ni_lock(ni); 92062306a36Sopenharmony_ci std = ni_std5(ni); 92162306a36Sopenharmony_ci if (!std) { 92262306a36Sopenharmony_ci err = -EINVAL; 92362306a36Sopenharmony_ci } else if (std->security_id != security_id) { 92462306a36Sopenharmony_ci std->security_id = ni->std_security_id = security_id; 92562306a36Sopenharmony_ci /* Std attribute always in primary record. */ 92662306a36Sopenharmony_ci ni->mi.dirty = true; 92762306a36Sopenharmony_ci mark_inode_dirty(&ni->vfs_inode); 92862306a36Sopenharmony_ci } 92962306a36Sopenharmony_ci ni_unlock(ni); 93062306a36Sopenharmony_ci goto out; 93162306a36Sopenharmony_ci } 93262306a36Sopenharmony_ci 93362306a36Sopenharmony_ci /* Deal with NTFS extended attribute. */ 93462306a36Sopenharmony_ci err = ntfs_set_ea(inode, name, strlen(name), value, size, flags, 0, 93562306a36Sopenharmony_ci NULL); 93662306a36Sopenharmony_ci 93762306a36Sopenharmony_ciout: 93862306a36Sopenharmony_ci inode_set_ctime_current(inode); 93962306a36Sopenharmony_ci mark_inode_dirty(inode); 94062306a36Sopenharmony_ci 94162306a36Sopenharmony_ci return err; 94262306a36Sopenharmony_ci} 94362306a36Sopenharmony_ci 94462306a36Sopenharmony_ci/* 94562306a36Sopenharmony_ci * ntfs_save_wsl_perm 94662306a36Sopenharmony_ci * 94762306a36Sopenharmony_ci * save uid/gid/mode in xattr 94862306a36Sopenharmony_ci */ 94962306a36Sopenharmony_ciint ntfs_save_wsl_perm(struct inode *inode, __le16 *ea_size) 95062306a36Sopenharmony_ci{ 95162306a36Sopenharmony_ci int err; 95262306a36Sopenharmony_ci __le32 value; 95362306a36Sopenharmony_ci struct ntfs_inode *ni = ntfs_i(inode); 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_ci ni_lock(ni); 95662306a36Sopenharmony_ci value = cpu_to_le32(i_uid_read(inode)); 95762306a36Sopenharmony_ci err = ntfs_set_ea(inode, "$LXUID", sizeof("$LXUID") - 1, &value, 95862306a36Sopenharmony_ci sizeof(value), 0, true, ea_size); 95962306a36Sopenharmony_ci if (err) 96062306a36Sopenharmony_ci goto out; 96162306a36Sopenharmony_ci 96262306a36Sopenharmony_ci value = cpu_to_le32(i_gid_read(inode)); 96362306a36Sopenharmony_ci err = ntfs_set_ea(inode, "$LXGID", sizeof("$LXGID") - 1, &value, 96462306a36Sopenharmony_ci sizeof(value), 0, true, ea_size); 96562306a36Sopenharmony_ci if (err) 96662306a36Sopenharmony_ci goto out; 96762306a36Sopenharmony_ci 96862306a36Sopenharmony_ci value = cpu_to_le32(inode->i_mode); 96962306a36Sopenharmony_ci err = ntfs_set_ea(inode, "$LXMOD", sizeof("$LXMOD") - 1, &value, 97062306a36Sopenharmony_ci sizeof(value), 0, true, ea_size); 97162306a36Sopenharmony_ci if (err) 97262306a36Sopenharmony_ci goto out; 97362306a36Sopenharmony_ci 97462306a36Sopenharmony_ci if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) { 97562306a36Sopenharmony_ci value = cpu_to_le32(inode->i_rdev); 97662306a36Sopenharmony_ci err = ntfs_set_ea(inode, "$LXDEV", sizeof("$LXDEV") - 1, &value, 97762306a36Sopenharmony_ci sizeof(value), 0, true, ea_size); 97862306a36Sopenharmony_ci if (err) 97962306a36Sopenharmony_ci goto out; 98062306a36Sopenharmony_ci } 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_ciout: 98362306a36Sopenharmony_ci ni_unlock(ni); 98462306a36Sopenharmony_ci /* In case of error should we delete all WSL xattr? */ 98562306a36Sopenharmony_ci return err; 98662306a36Sopenharmony_ci} 98762306a36Sopenharmony_ci 98862306a36Sopenharmony_ci/* 98962306a36Sopenharmony_ci * ntfs_get_wsl_perm 99062306a36Sopenharmony_ci * 99162306a36Sopenharmony_ci * get uid/gid/mode from xattr 99262306a36Sopenharmony_ci * it is called from ntfs_iget5->ntfs_read_mft 99362306a36Sopenharmony_ci */ 99462306a36Sopenharmony_civoid ntfs_get_wsl_perm(struct inode *inode) 99562306a36Sopenharmony_ci{ 99662306a36Sopenharmony_ci size_t sz; 99762306a36Sopenharmony_ci __le32 value[3]; 99862306a36Sopenharmony_ci 99962306a36Sopenharmony_ci if (ntfs_get_ea(inode, "$LXUID", sizeof("$LXUID") - 1, &value[0], 100062306a36Sopenharmony_ci sizeof(value[0]), &sz) == sizeof(value[0]) && 100162306a36Sopenharmony_ci ntfs_get_ea(inode, "$LXGID", sizeof("$LXGID") - 1, &value[1], 100262306a36Sopenharmony_ci sizeof(value[1]), &sz) == sizeof(value[1]) && 100362306a36Sopenharmony_ci ntfs_get_ea(inode, "$LXMOD", sizeof("$LXMOD") - 1, &value[2], 100462306a36Sopenharmony_ci sizeof(value[2]), &sz) == sizeof(value[2])) { 100562306a36Sopenharmony_ci i_uid_write(inode, (uid_t)le32_to_cpu(value[0])); 100662306a36Sopenharmony_ci i_gid_write(inode, (gid_t)le32_to_cpu(value[1])); 100762306a36Sopenharmony_ci inode->i_mode = le32_to_cpu(value[2]); 100862306a36Sopenharmony_ci 100962306a36Sopenharmony_ci if (ntfs_get_ea(inode, "$LXDEV", sizeof("$$LXDEV") - 1, 101062306a36Sopenharmony_ci &value[0], sizeof(value), 101162306a36Sopenharmony_ci &sz) == sizeof(value[0])) { 101262306a36Sopenharmony_ci inode->i_rdev = le32_to_cpu(value[0]); 101362306a36Sopenharmony_ci } 101462306a36Sopenharmony_ci } 101562306a36Sopenharmony_ci} 101662306a36Sopenharmony_ci 101762306a36Sopenharmony_cistatic bool ntfs_xattr_user_list(struct dentry *dentry) 101862306a36Sopenharmony_ci{ 101962306a36Sopenharmony_ci return true; 102062306a36Sopenharmony_ci} 102162306a36Sopenharmony_ci 102262306a36Sopenharmony_ci// clang-format off 102362306a36Sopenharmony_cistatic const struct xattr_handler ntfs_other_xattr_handler = { 102462306a36Sopenharmony_ci .prefix = "", 102562306a36Sopenharmony_ci .get = ntfs_getxattr, 102662306a36Sopenharmony_ci .set = ntfs_setxattr, 102762306a36Sopenharmony_ci .list = ntfs_xattr_user_list, 102862306a36Sopenharmony_ci}; 102962306a36Sopenharmony_ci 103062306a36Sopenharmony_ciconst struct xattr_handler *ntfs_xattr_handlers[] = { 103162306a36Sopenharmony_ci &ntfs_other_xattr_handler, 103262306a36Sopenharmony_ci NULL, 103362306a36Sopenharmony_ci}; 103462306a36Sopenharmony_ci// clang-format on 1035