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