diff options
author | Vyacheslav Dubeyko <slava@dubeyko.com> | 2013-11-12 18:11:09 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2013-11-12 22:09:32 -0500 |
commit | 95e0d7dbb9b28ab0dfad7c7316066b05e1f1d4cd (patch) | |
tree | 09f42df8169a4fbf3e2f60cb1b36c1e83c8d9019 /fs/hfsplus/xattr.c | |
parent | 099e9245e04d50bb12ed621b4fa61df0a4c9dba9 (diff) |
hfsplus: implement attributes file creation functionality
Implement functionality of creation AttributesFile metadata file on HFS+
volume in the case of absence of it.
It makes trying to open AttributesFile's B-tree during mount of HFS+
volume. If HFS+ volume hasn't AttributesFile then a pointer on
AttributesFile's B-tree keeps as NULL. Thereby, when it is discovered
absence of AttributesFile on HFS+ volume in the begin of xattr creation
operation then AttributesFile will be created.
The creation of AttributesFile will have success in the case of
availability (2 * clump) free blocks on HFS+ volume. Otherwise,
creation operation is ended with error (-ENOSPC).
Signed-off-by: Vyacheslav Dubeyko <slava@dubeyko.com>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: Christoph Hellwig <hch@infradead.org>
Acked-by: Hin-Tak Leung <htl10@users.sourceforge.net>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'fs/hfsplus/xattr.c')
-rw-r--r-- | fs/hfsplus/xattr.c | 142 |
1 files changed, 140 insertions, 2 deletions
diff --git a/fs/hfsplus/xattr.c b/fs/hfsplus/xattr.c index 568a45cb1145..efc85b1377cc 100644 --- a/fs/hfsplus/xattr.c +++ b/fs/hfsplus/xattr.c | |||
@@ -192,6 +192,143 @@ static void hfsplus_init_header_node(struct inode *attr_file, | |||
192 | *--rec_offsets = cpu_to_be16(offset); | 192 | *--rec_offsets = cpu_to_be16(offset); |
193 | } | 193 | } |
194 | 194 | ||
195 | static int hfsplus_create_attributes_file(struct super_block *sb) | ||
196 | { | ||
197 | int err = 0; | ||
198 | struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb); | ||
199 | struct inode *attr_file; | ||
200 | struct hfsplus_inode_info *hip; | ||
201 | u32 clump_size; | ||
202 | u16 node_size = HFSPLUS_ATTR_TREE_NODE_SIZE; | ||
203 | char *buf; | ||
204 | int index, written; | ||
205 | struct address_space *mapping; | ||
206 | struct page *page; | ||
207 | int old_state = HFSPLUS_EMPTY_ATTR_TREE; | ||
208 | |||
209 | hfs_dbg(ATTR_MOD, "create_attr_file: ino %d\n", HFSPLUS_ATTR_CNID); | ||
210 | |||
211 | check_attr_tree_state_again: | ||
212 | switch (atomic_read(&sbi->attr_tree_state)) { | ||
213 | case HFSPLUS_EMPTY_ATTR_TREE: | ||
214 | if (old_state != atomic_cmpxchg(&sbi->attr_tree_state, | ||
215 | old_state, | ||
216 | HFSPLUS_CREATING_ATTR_TREE)) | ||
217 | goto check_attr_tree_state_again; | ||
218 | break; | ||
219 | case HFSPLUS_CREATING_ATTR_TREE: | ||
220 | /* | ||
221 | * This state means that another thread is in process | ||
222 | * of AttributesFile creation. Theoretically, it is | ||
223 | * possible to be here. But really __setxattr() method | ||
224 | * first of all calls hfs_find_init() for lookup in | ||
225 | * B-tree of CatalogFile. This method locks mutex of | ||
226 | * CatalogFile's B-tree. As a result, if some thread | ||
227 | * is inside AttributedFile creation operation then | ||
228 | * another threads will be waiting unlocking of | ||
229 | * CatalogFile's B-tree's mutex. However, if code will | ||
230 | * change then we will return error code (-EAGAIN) from | ||
231 | * here. Really, it means that first try to set of xattr | ||
232 | * fails with error but second attempt will have success. | ||
233 | */ | ||
234 | return -EAGAIN; | ||
235 | case HFSPLUS_VALID_ATTR_TREE: | ||
236 | return 0; | ||
237 | case HFSPLUS_FAILED_ATTR_TREE: | ||
238 | return -EOPNOTSUPP; | ||
239 | default: | ||
240 | BUG(); | ||
241 | } | ||
242 | |||
243 | attr_file = hfsplus_iget(sb, HFSPLUS_ATTR_CNID); | ||
244 | if (IS_ERR(attr_file)) { | ||
245 | pr_err("failed to load attributes file\n"); | ||
246 | return PTR_ERR(attr_file); | ||
247 | } | ||
248 | |||
249 | BUG_ON(i_size_read(attr_file) != 0); | ||
250 | |||
251 | hip = HFSPLUS_I(attr_file); | ||
252 | |||
253 | clump_size = hfsplus_calc_btree_clump_size(sb->s_blocksize, | ||
254 | node_size, | ||
255 | sbi->sect_count, | ||
256 | HFSPLUS_ATTR_CNID); | ||
257 | |||
258 | mutex_lock(&hip->extents_lock); | ||
259 | hip->clump_blocks = clump_size >> sbi->alloc_blksz_shift; | ||
260 | mutex_unlock(&hip->extents_lock); | ||
261 | |||
262 | if (sbi->free_blocks <= (hip->clump_blocks << 1)) { | ||
263 | err = -ENOSPC; | ||
264 | goto end_attr_file_creation; | ||
265 | } | ||
266 | |||
267 | while (hip->alloc_blocks < hip->clump_blocks) { | ||
268 | err = hfsplus_file_extend(attr_file); | ||
269 | if (unlikely(err)) { | ||
270 | pr_err("failed to extend attributes file\n"); | ||
271 | goto end_attr_file_creation; | ||
272 | } | ||
273 | hip->phys_size = attr_file->i_size = | ||
274 | (loff_t)hip->alloc_blocks << sbi->alloc_blksz_shift; | ||
275 | hip->fs_blocks = hip->alloc_blocks << sbi->fs_shift; | ||
276 | inode_set_bytes(attr_file, attr_file->i_size); | ||
277 | } | ||
278 | |||
279 | buf = kzalloc(node_size, GFP_NOFS); | ||
280 | if (!buf) { | ||
281 | pr_err("failed to allocate memory for header node\n"); | ||
282 | err = -ENOMEM; | ||
283 | goto end_attr_file_creation; | ||
284 | } | ||
285 | |||
286 | hfsplus_init_header_node(attr_file, clump_size, buf, node_size); | ||
287 | |||
288 | mapping = attr_file->i_mapping; | ||
289 | |||
290 | index = 0; | ||
291 | written = 0; | ||
292 | for (; written < node_size; index++, written += PAGE_CACHE_SIZE) { | ||
293 | void *kaddr; | ||
294 | |||
295 | page = read_mapping_page(mapping, index, NULL); | ||
296 | if (IS_ERR(page)) { | ||
297 | err = PTR_ERR(page); | ||
298 | goto failed_header_node_init; | ||
299 | } | ||
300 | |||
301 | kaddr = kmap_atomic(page); | ||
302 | memcpy(kaddr, buf + written, | ||
303 | min_t(size_t, PAGE_CACHE_SIZE, node_size - written)); | ||
304 | kunmap_atomic(kaddr); | ||
305 | |||
306 | set_page_dirty(page); | ||
307 | page_cache_release(page); | ||
308 | } | ||
309 | |||
310 | hfsplus_mark_inode_dirty(attr_file, HFSPLUS_I_ATTR_DIRTY); | ||
311 | |||
312 | sbi->attr_tree = hfs_btree_open(sb, HFSPLUS_ATTR_CNID); | ||
313 | if (!sbi->attr_tree) | ||
314 | pr_err("failed to load attributes file\n"); | ||
315 | |||
316 | failed_header_node_init: | ||
317 | kfree(buf); | ||
318 | |||
319 | end_attr_file_creation: | ||
320 | iput(attr_file); | ||
321 | |||
322 | if (!err) | ||
323 | atomic_set(&sbi->attr_tree_state, HFSPLUS_VALID_ATTR_TREE); | ||
324 | else if (err == -ENOSPC) | ||
325 | atomic_set(&sbi->attr_tree_state, HFSPLUS_EMPTY_ATTR_TREE); | ||
326 | else | ||
327 | atomic_set(&sbi->attr_tree_state, HFSPLUS_FAILED_ATTR_TREE); | ||
328 | |||
329 | return err; | ||
330 | } | ||
331 | |||
195 | int __hfsplus_setxattr(struct inode *inode, const char *name, | 332 | int __hfsplus_setxattr(struct inode *inode, const char *name, |
196 | const void *value, size_t size, int flags) | 333 | const void *value, size_t size, int flags) |
197 | { | 334 | { |
@@ -276,8 +413,9 @@ int __hfsplus_setxattr(struct inode *inode, const char *name, | |||
276 | } | 413 | } |
277 | 414 | ||
278 | if (!HFSPLUS_SB(inode->i_sb)->attr_tree) { | 415 | if (!HFSPLUS_SB(inode->i_sb)->attr_tree) { |
279 | err = -EOPNOTSUPP; | 416 | err = hfsplus_create_attributes_file(inode->i_sb); |
280 | goto end_setxattr; | 417 | if (unlikely(err)) |
418 | goto end_setxattr; | ||
281 | } | 419 | } |
282 | 420 | ||
283 | if (hfsplus_attr_exists(inode, name)) { | 421 | if (hfsplus_attr_exists(inode, name)) { |