attributes.c 9.2 KB

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