Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

hfsplus: add functionality of manipulating by records in attributes tree

Add functionality of manipulating by records in attributes tree.

Signed-off-by: Vyacheslav Dubeyko <slava@dubeyko.com>
Reported-by: Hin-Tak Leung <htl10@users.sourceforge.net>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: Christoph Hellwig <hch@lst.de>
Cc: Jan Kara <jack@suse.cz>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

authored by

Vyacheslav Dubeyko and committed by
Linus Torvalds
3e05ca20 9ed083d8

+399
+399
fs/hfsplus/attributes.c
··· 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 + }