1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * linux/fs/hfsplus/attributes.c 4 * 5 * Vyacheslav Dubeyko <slava@dubeyko.com> 6 * 7 * Handling of records in attributes tree 8 */ 9 10 #include "hfsplus_fs.h" 11 #include "hfsplus_raw.h" 12 13 static struct kmem_cache *hfsplus_attr_tree_cachep; 14 15 int __init hfsplus_create_attr_tree_cache(void) 16 { 17 if (hfsplus_attr_tree_cachep) 18 return -EEXIST; 19 20 hfsplus_attr_tree_cachep = 21 kmem_cache_create("hfsplus_attr_cache", 22 sizeof(hfsplus_attr_entry), 0, 23 SLAB_HWCACHE_ALIGN, NULL); 24 if (!hfsplus_attr_tree_cachep) 25 return -ENOMEM; 26 27 return 0; 28 } 29 30 void hfsplus_destroy_attr_tree_cache(void) 31 { 32 kmem_cache_destroy(hfsplus_attr_tree_cachep); 33 } 34 35 int hfsplus_attr_bin_cmp_key(const hfsplus_btree_key *k1, 36 const hfsplus_btree_key *k2) 37 { 38 __be32 k1_cnid, k2_cnid; 39 40 k1_cnid = k1->attr.cnid; 41 k2_cnid = k2->attr.cnid; 42 if (k1_cnid != k2_cnid) 43 return be32_to_cpu(k1_cnid) < be32_to_cpu(k2_cnid) ? -1 : 1; 44 45 return hfsplus_strcmp( 46 (const struct hfsplus_unistr *)&k1->attr.key_name, 47 (const struct hfsplus_unistr *)&k2->attr.key_name); 48 } 49 50 int hfsplus_attr_build_key(struct super_block *sb, hfsplus_btree_key *key, 51 u32 cnid, const char *name) 52 { 53 int len; 54 55 memset(key, 0, sizeof(struct hfsplus_attr_key)); 56 key->attr.cnid = cpu_to_be32(cnid); 57 if (name) { 58 int res = hfsplus_asc2uni(sb, 59 (struct hfsplus_unistr *)&key->attr.key_name, 60 HFSPLUS_ATTR_MAX_STRLEN, name, strlen(name)); 61 if (res) 62 return res; 63 len = be16_to_cpu(key->attr.key_name.length); 64 } else { 65 key->attr.key_name.length = 0; 66 len = 0; 67 } 68 69 /* The length of the key, as stored in key_len field, does not include 70 * the size of the key_len field itself. 71 * So, offsetof(hfsplus_attr_key, key_name) is a trick because 72 * it takes into consideration key_len field (__be16) of 73 * hfsplus_attr_key structure instead of length field (__be16) of 74 * hfsplus_attr_unistr structure. 75 */ 76 key->key_len = 77 cpu_to_be16(offsetof(struct hfsplus_attr_key, key_name) + 78 2 * len); 79 80 return 0; 81 } 82 83 hfsplus_attr_entry *hfsplus_alloc_attr_entry(void) 84 { 85 return kmem_cache_alloc(hfsplus_attr_tree_cachep, GFP_KERNEL); 86 } 87 88 void hfsplus_destroy_attr_entry(hfsplus_attr_entry *entry) 89 { 90 if (entry) 91 kmem_cache_free(hfsplus_attr_tree_cachep, entry); 92 } 93 94 #define HFSPLUS_INVALID_ATTR_RECORD -1 95 96 static int hfsplus_attr_build_record(hfsplus_attr_entry *entry, int record_type, 97 u32 cnid, const void *value, size_t size) 98 { 99 if (record_type == HFSPLUS_ATTR_FORK_DATA) { 100 /* 101 * Mac OS X supports only inline data attributes. 102 * Do nothing 103 */ 104 memset(entry, 0, sizeof(*entry)); 105 return sizeof(struct hfsplus_attr_fork_data); 106 } else if (record_type == HFSPLUS_ATTR_EXTENTS) { 107 /* 108 * Mac OS X supports only inline data attributes. 109 * Do nothing. 110 */ 111 memset(entry, 0, sizeof(*entry)); 112 return sizeof(struct hfsplus_attr_extents); 113 } else if (record_type == HFSPLUS_ATTR_INLINE_DATA) { 114 u16 len; 115 116 memset(entry, 0, sizeof(struct hfsplus_attr_inline_data)); 117 entry->inline_data.record_type = cpu_to_be32(record_type); 118 if (size <= HFSPLUS_MAX_INLINE_DATA_SIZE) 119 len = size; 120 else { 121 hfs_dbg("value size %zu is too big\n", size); 122 return HFSPLUS_INVALID_ATTR_RECORD; 123 } 124 entry->inline_data.length = cpu_to_be16(len); 125 memcpy(entry->inline_data.raw_bytes, value, len); 126 /* 127 * Align len on two-byte boundary. 128 * It needs to add pad byte if we have odd len. 129 */ 130 len = round_up(len, 2); 131 return offsetof(struct hfsplus_attr_inline_data, raw_bytes) + 132 len; 133 } else /* invalid input */ 134 memset(entry, 0, sizeof(*entry)); 135 136 return HFSPLUS_INVALID_ATTR_RECORD; 137 } 138 139 int hfsplus_find_attr(struct super_block *sb, u32 cnid, 140 const char *name, struct hfs_find_data *fd) 141 { 142 int err = 0; 143 144 hfs_dbg("name %s, cnid %d\n", name ? name : NULL, cnid); 145 146 if (!HFSPLUS_SB(sb)->attr_tree) { 147 pr_err("attributes file doesn't exist\n"); 148 return -EINVAL; 149 } 150 151 if (name) { 152 err = hfsplus_attr_build_key(sb, fd->search_key, cnid, name); 153 if (err) 154 goto failed_find_attr; 155 err = hfs_brec_find(fd, hfs_find_rec_by_key); 156 if (err) 157 goto failed_find_attr; 158 } else { 159 err = hfsplus_attr_build_key(sb, fd->search_key, cnid, NULL); 160 if (err) 161 goto failed_find_attr; 162 err = hfs_brec_find(fd, hfs_find_1st_rec_by_cnid); 163 if (err) 164 goto failed_find_attr; 165 } 166 167 failed_find_attr: 168 return err; 169 } 170 171 int hfsplus_attr_exists(struct inode *inode, const char *name) 172 { 173 int err = 0; 174 struct super_block *sb = inode->i_sb; 175 struct hfs_find_data fd; 176 177 if (!HFSPLUS_SB(sb)->attr_tree) 178 return 0; 179 180 err = hfs_find_init(HFSPLUS_SB(sb)->attr_tree, &fd); 181 if (err) 182 return 0; 183 184 err = hfsplus_find_attr(sb, inode->i_ino, name, &fd); 185 if (err) 186 goto attr_not_found; 187 188 hfs_find_exit(&fd); 189 return 1; 190 191 attr_not_found: 192 hfs_find_exit(&fd); 193 return 0; 194 } 195 196 static 197 int hfsplus_create_attr_nolock(struct inode *inode, const char *name, 198 const void *value, size_t size, 199 struct hfs_find_data *fd, 200 hfsplus_attr_entry *entry_ptr) 201 { 202 struct super_block *sb = inode->i_sb; 203 int entry_size; 204 int err; 205 206 hfs_dbg("name %s, ino %ld\n", 207 name ? name : NULL, inode->i_ino); 208 209 if (name) { 210 err = hfsplus_attr_build_key(sb, fd->search_key, 211 inode->i_ino, name); 212 if (err) 213 return err; 214 } else 215 return -EINVAL; 216 217 /* Mac OS X supports only inline data attributes. */ 218 entry_size = hfsplus_attr_build_record(entry_ptr, 219 HFSPLUS_ATTR_INLINE_DATA, 220 inode->i_ino, 221 value, size); 222 if (entry_size == HFSPLUS_INVALID_ATTR_RECORD) { 223 if (size > HFSPLUS_MAX_INLINE_DATA_SIZE) 224 err = -E2BIG; 225 else 226 err = -EINVAL; 227 hfs_dbg("unable to store value: err %d\n", err); 228 return err; 229 } 230 231 err = hfs_brec_find(fd, hfs_find_rec_by_key); 232 if (err != -ENOENT) { 233 if (!err) 234 err = -EEXIST; 235 return err; 236 } 237 238 err = hfs_brec_insert(fd, entry_ptr, entry_size); 239 if (err) { 240 hfs_dbg("unable to store value: err %d\n", err); 241 return err; 242 } 243 244 hfsplus_mark_inode_dirty(inode, HFSPLUS_I_ATTR_DIRTY); 245 246 return 0; 247 } 248 249 int hfsplus_create_attr(struct inode *inode, 250 const char *name, 251 const void *value, size_t size) 252 { 253 struct super_block *sb = inode->i_sb; 254 struct hfs_find_data fd; 255 hfsplus_attr_entry *entry_ptr; 256 int err; 257 258 hfs_dbg("name %s, ino %ld\n", 259 name ? name : NULL, inode->i_ino); 260 261 if (!HFSPLUS_SB(sb)->attr_tree) { 262 pr_err("attributes file doesn't exist\n"); 263 return -EINVAL; 264 } 265 266 entry_ptr = hfsplus_alloc_attr_entry(); 267 if (!entry_ptr) 268 return -ENOMEM; 269 270 err = hfs_find_init(HFSPLUS_SB(sb)->attr_tree, &fd); 271 if (err) 272 goto failed_init_create_attr; 273 274 /* Fail early and avoid ENOSPC during the btree operation */ 275 err = hfs_bmap_reserve(fd.tree, fd.tree->depth + 1); 276 if (err) 277 goto failed_create_attr; 278 279 err = hfsplus_create_attr_nolock(inode, name, value, size, 280 &fd, entry_ptr); 281 if (err) 282 goto failed_create_attr; 283 284 failed_create_attr: 285 hfs_find_exit(&fd); 286 287 failed_init_create_attr: 288 hfsplus_destroy_attr_entry(entry_ptr); 289 return err; 290 } 291 292 static int __hfsplus_delete_attr(struct inode *inode, u32 cnid, 293 struct hfs_find_data *fd) 294 { 295 int err = 0; 296 __be32 found_cnid, record_type; 297 298 hfs_bnode_read(fd->bnode, &found_cnid, 299 fd->keyoffset + 300 offsetof(struct hfsplus_attr_key, cnid), 301 sizeof(__be32)); 302 if (cnid != be32_to_cpu(found_cnid)) 303 return -ENOENT; 304 305 hfs_bnode_read(fd->bnode, &record_type, 306 fd->entryoffset, sizeof(record_type)); 307 308 switch (be32_to_cpu(record_type)) { 309 case HFSPLUS_ATTR_INLINE_DATA: 310 /* All is OK. Do nothing. */ 311 break; 312 case HFSPLUS_ATTR_FORK_DATA: 313 case HFSPLUS_ATTR_EXTENTS: 314 pr_err("only inline data xattr are supported\n"); 315 return -EOPNOTSUPP; 316 default: 317 pr_err("invalid extended attribute record\n"); 318 return -ENOENT; 319 } 320 321 /* Avoid btree corruption */ 322 hfs_bnode_read(fd->bnode, fd->search_key, 323 fd->keyoffset, fd->keylength); 324 325 err = hfs_brec_remove(fd); 326 if (err) 327 return err; 328 329 hfsplus_mark_inode_dirty(inode, HFSPLUS_I_ATTR_DIRTY); 330 return err; 331 } 332 333 static 334 int hfsplus_delete_attr_nolock(struct inode *inode, const char *name, 335 struct hfs_find_data *fd) 336 { 337 struct super_block *sb = inode->i_sb; 338 int err; 339 340 hfs_dbg("name %s, ino %ld\n", 341 name ? name : NULL, inode->i_ino); 342 343 if (name) { 344 err = hfsplus_attr_build_key(sb, fd->search_key, 345 inode->i_ino, name); 346 if (err) 347 return err; 348 } else { 349 pr_err("invalid extended attribute name\n"); 350 return -EINVAL; 351 } 352 353 err = hfs_brec_find(fd, hfs_find_rec_by_key); 354 if (err) 355 return err; 356 357 err = __hfsplus_delete_attr(inode, inode->i_ino, fd); 358 if (err) 359 return err; 360 361 return 0; 362 } 363 364 int hfsplus_delete_attr(struct inode *inode, const char *name) 365 { 366 int err = 0; 367 struct super_block *sb = inode->i_sb; 368 struct hfs_find_data fd; 369 370 hfs_dbg("name %s, ino %ld\n", 371 name ? name : NULL, inode->i_ino); 372 373 if (!HFSPLUS_SB(sb)->attr_tree) { 374 pr_err("attributes file doesn't exist\n"); 375 return -EINVAL; 376 } 377 378 err = hfs_find_init(HFSPLUS_SB(sb)->attr_tree, &fd); 379 if (err) 380 return err; 381 382 /* Fail early and avoid ENOSPC during the btree operation */ 383 err = hfs_bmap_reserve(fd.tree, fd.tree->depth); 384 if (err) 385 goto out; 386 387 err = hfsplus_delete_attr_nolock(inode, name, &fd); 388 if (err) 389 goto out; 390 391 out: 392 hfs_find_exit(&fd); 393 return err; 394 } 395 396 int hfsplus_delete_all_attrs(struct inode *dir, u32 cnid) 397 { 398 int err = 0; 399 struct hfs_find_data fd; 400 401 hfs_dbg("cnid %d\n", cnid); 402 403 if (!HFSPLUS_SB(dir->i_sb)->attr_tree) { 404 pr_err("attributes file doesn't exist\n"); 405 return -EINVAL; 406 } 407 408 err = hfs_find_init(HFSPLUS_SB(dir->i_sb)->attr_tree, &fd); 409 if (err) 410 return err; 411 412 for (;;) { 413 err = hfsplus_find_attr(dir->i_sb, cnid, NULL, &fd); 414 if (err) { 415 if (err != -ENOENT) 416 pr_err("xattr search failed\n"); 417 goto end_delete_all; 418 } 419 420 err = __hfsplus_delete_attr(dir, cnid, &fd); 421 if (err) 422 goto end_delete_all; 423 } 424 425 end_delete_all: 426 hfs_find_exit(&fd); 427 return err; 428 } 429 430 int hfsplus_replace_attr(struct inode *inode, 431 const char *name, 432 const void *value, size_t size) 433 { 434 struct super_block *sb = inode->i_sb; 435 struct hfs_find_data fd; 436 hfsplus_attr_entry *entry_ptr; 437 int err = 0; 438 439 hfs_dbg("name %s, ino %ld\n", 440 name ? name : NULL, inode->i_ino); 441 442 if (!HFSPLUS_SB(sb)->attr_tree) { 443 pr_err("attributes file doesn't exist\n"); 444 return -EINVAL; 445 } 446 447 entry_ptr = hfsplus_alloc_attr_entry(); 448 if (!entry_ptr) 449 return -ENOMEM; 450 451 err = hfs_find_init(HFSPLUS_SB(sb)->attr_tree, &fd); 452 if (err) 453 goto failed_init_replace_attr; 454 455 /* Fail early and avoid ENOSPC during the btree operation */ 456 err = hfs_bmap_reserve(fd.tree, fd.tree->depth + 1); 457 if (err) 458 goto failed_replace_attr; 459 460 err = hfsplus_delete_attr_nolock(inode, name, &fd); 461 if (err) 462 goto failed_replace_attr; 463 464 err = hfsplus_create_attr_nolock(inode, name, value, size, 465 &fd, entry_ptr); 466 if (err) 467 goto failed_replace_attr; 468 469 failed_replace_attr: 470 hfs_find_exit(&fd); 471 472 failed_init_replace_attr: 473 hfsplus_destroy_attr_entry(entry_ptr); 474 return err; 475 } 476