diff options
Diffstat (limited to 'fs/hfsplus/super.c')
-rw-r--r-- | fs/hfsplus/super.c | 130 |
1 files changed, 79 insertions, 51 deletions
diff --git a/fs/hfsplus/super.c b/fs/hfsplus/super.c index ddf712e4700e..6ee6ad20acf2 100644 --- a/fs/hfsplus/super.c +++ b/fs/hfsplus/super.c | |||
@@ -10,6 +10,7 @@ | |||
10 | #include <linux/module.h> | 10 | #include <linux/module.h> |
11 | #include <linux/init.h> | 11 | #include <linux/init.h> |
12 | #include <linux/pagemap.h> | 12 | #include <linux/pagemap.h> |
13 | #include <linux/blkdev.h> | ||
13 | #include <linux/fs.h> | 14 | #include <linux/fs.h> |
14 | #include <linux/slab.h> | 15 | #include <linux/slab.h> |
15 | #include <linux/vfs.h> | 16 | #include <linux/vfs.h> |
@@ -66,6 +67,7 @@ struct inode *hfsplus_iget(struct super_block *sb, unsigned long ino) | |||
66 | INIT_LIST_HEAD(&HFSPLUS_I(inode)->open_dir_list); | 67 | INIT_LIST_HEAD(&HFSPLUS_I(inode)->open_dir_list); |
67 | mutex_init(&HFSPLUS_I(inode)->extents_lock); | 68 | mutex_init(&HFSPLUS_I(inode)->extents_lock); |
68 | HFSPLUS_I(inode)->flags = 0; | 69 | HFSPLUS_I(inode)->flags = 0; |
70 | HFSPLUS_I(inode)->extent_state = 0; | ||
69 | HFSPLUS_I(inode)->rsrc_inode = NULL; | 71 | HFSPLUS_I(inode)->rsrc_inode = NULL; |
70 | atomic_set(&HFSPLUS_I(inode)->opencnt, 0); | 72 | atomic_set(&HFSPLUS_I(inode)->opencnt, 0); |
71 | 73 | ||
@@ -157,45 +159,65 @@ int hfsplus_sync_fs(struct super_block *sb, int wait) | |||
157 | { | 159 | { |
158 | struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb); | 160 | struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb); |
159 | struct hfsplus_vh *vhdr = sbi->s_vhdr; | 161 | struct hfsplus_vh *vhdr = sbi->s_vhdr; |
162 | int write_backup = 0; | ||
163 | int error, error2; | ||
164 | |||
165 | if (!wait) | ||
166 | return 0; | ||
160 | 167 | ||
161 | dprint(DBG_SUPER, "hfsplus_write_super\n"); | 168 | dprint(DBG_SUPER, "hfsplus_write_super\n"); |
162 | 169 | ||
163 | mutex_lock(&sbi->vh_mutex); | ||
164 | mutex_lock(&sbi->alloc_mutex); | ||
165 | sb->s_dirt = 0; | 170 | sb->s_dirt = 0; |
166 | 171 | ||
172 | /* | ||
173 | * Explicitly write out the special metadata inodes. | ||
174 | * | ||
175 | * While these special inodes are marked as hashed and written | ||
176 | * out peridocically by the flusher threads we redirty them | ||
177 | * during writeout of normal inodes, and thus the life lock | ||
178 | * prevents us from getting the latest state to disk. | ||
179 | */ | ||
180 | error = filemap_write_and_wait(sbi->cat_tree->inode->i_mapping); | ||
181 | error2 = filemap_write_and_wait(sbi->ext_tree->inode->i_mapping); | ||
182 | if (!error) | ||
183 | error = error2; | ||
184 | error2 = filemap_write_and_wait(sbi->alloc_file->i_mapping); | ||
185 | if (!error) | ||
186 | error = error2; | ||
187 | |||
188 | mutex_lock(&sbi->vh_mutex); | ||
189 | mutex_lock(&sbi->alloc_mutex); | ||
167 | vhdr->free_blocks = cpu_to_be32(sbi->free_blocks); | 190 | vhdr->free_blocks = cpu_to_be32(sbi->free_blocks); |
168 | vhdr->next_cnid = cpu_to_be32(sbi->next_cnid); | 191 | vhdr->next_cnid = cpu_to_be32(sbi->next_cnid); |
169 | vhdr->folder_count = cpu_to_be32(sbi->folder_count); | 192 | vhdr->folder_count = cpu_to_be32(sbi->folder_count); |
170 | vhdr->file_count = cpu_to_be32(sbi->file_count); | 193 | vhdr->file_count = cpu_to_be32(sbi->file_count); |
171 | 194 | ||
172 | mark_buffer_dirty(sbi->s_vhbh); | ||
173 | if (test_and_clear_bit(HFSPLUS_SB_WRITEBACKUP, &sbi->flags)) { | 195 | if (test_and_clear_bit(HFSPLUS_SB_WRITEBACKUP, &sbi->flags)) { |
174 | if (sbi->sect_count) { | 196 | memcpy(sbi->s_backup_vhdr, sbi->s_vhdr, sizeof(*sbi->s_vhdr)); |
175 | struct buffer_head *bh; | 197 | write_backup = 1; |
176 | u32 block, offset; | ||
177 | |||
178 | block = sbi->blockoffset; | ||
179 | block += (sbi->sect_count - 2) >> (sb->s_blocksize_bits - 9); | ||
180 | offset = ((sbi->sect_count - 2) << 9) & (sb->s_blocksize - 1); | ||
181 | printk(KERN_DEBUG "hfs: backup: %u,%u,%u,%u\n", | ||
182 | sbi->blockoffset, sbi->sect_count, | ||
183 | block, offset); | ||
184 | bh = sb_bread(sb, block); | ||
185 | if (bh) { | ||
186 | vhdr = (struct hfsplus_vh *)(bh->b_data + offset); | ||
187 | if (be16_to_cpu(vhdr->signature) == HFSPLUS_VOLHEAD_SIG) { | ||
188 | memcpy(vhdr, sbi->s_vhdr, sizeof(*vhdr)); | ||
189 | mark_buffer_dirty(bh); | ||
190 | brelse(bh); | ||
191 | } else | ||
192 | printk(KERN_WARNING "hfs: backup not found!\n"); | ||
193 | } | ||
194 | } | ||
195 | } | 198 | } |
199 | |||
200 | error2 = hfsplus_submit_bio(sb->s_bdev, | ||
201 | sbi->part_start + HFSPLUS_VOLHEAD_SECTOR, | ||
202 | sbi->s_vhdr, WRITE_SYNC); | ||
203 | if (!error) | ||
204 | error = error2; | ||
205 | if (!write_backup) | ||
206 | goto out; | ||
207 | |||
208 | error2 = hfsplus_submit_bio(sb->s_bdev, | ||
209 | sbi->part_start + sbi->sect_count - 2, | ||
210 | sbi->s_backup_vhdr, WRITE_SYNC); | ||
211 | if (!error) | ||
212 | error2 = error; | ||
213 | out: | ||
196 | mutex_unlock(&sbi->alloc_mutex); | 214 | mutex_unlock(&sbi->alloc_mutex); |
197 | mutex_unlock(&sbi->vh_mutex); | 215 | mutex_unlock(&sbi->vh_mutex); |
198 | return 0; | 216 | |
217 | if (!test_bit(HFSPLUS_SB_NOBARRIER, &sbi->flags)) | ||
218 | blkdev_issue_flush(sb->s_bdev, GFP_KERNEL, NULL); | ||
219 | |||
220 | return error; | ||
199 | } | 221 | } |
200 | 222 | ||
201 | static void hfsplus_write_super(struct super_block *sb) | 223 | static void hfsplus_write_super(struct super_block *sb) |
@@ -215,23 +237,22 @@ static void hfsplus_put_super(struct super_block *sb) | |||
215 | if (!sb->s_fs_info) | 237 | if (!sb->s_fs_info) |
216 | return; | 238 | return; |
217 | 239 | ||
218 | if (sb->s_dirt) | ||
219 | hfsplus_write_super(sb); | ||
220 | if (!(sb->s_flags & MS_RDONLY) && sbi->s_vhdr) { | 240 | if (!(sb->s_flags & MS_RDONLY) && sbi->s_vhdr) { |
221 | struct hfsplus_vh *vhdr = sbi->s_vhdr; | 241 | struct hfsplus_vh *vhdr = sbi->s_vhdr; |
222 | 242 | ||
223 | vhdr->modify_date = hfsp_now2mt(); | 243 | vhdr->modify_date = hfsp_now2mt(); |
224 | vhdr->attributes |= cpu_to_be32(HFSPLUS_VOL_UNMNT); | 244 | vhdr->attributes |= cpu_to_be32(HFSPLUS_VOL_UNMNT); |
225 | vhdr->attributes &= cpu_to_be32(~HFSPLUS_VOL_INCNSTNT); | 245 | vhdr->attributes &= cpu_to_be32(~HFSPLUS_VOL_INCNSTNT); |
226 | mark_buffer_dirty(sbi->s_vhbh); | 246 | |
227 | sync_dirty_buffer(sbi->s_vhbh); | 247 | hfsplus_sync_fs(sb, 1); |
228 | } | 248 | } |
229 | 249 | ||
230 | hfs_btree_close(sbi->cat_tree); | 250 | hfs_btree_close(sbi->cat_tree); |
231 | hfs_btree_close(sbi->ext_tree); | 251 | hfs_btree_close(sbi->ext_tree); |
232 | iput(sbi->alloc_file); | 252 | iput(sbi->alloc_file); |
233 | iput(sbi->hidden_dir); | 253 | iput(sbi->hidden_dir); |
234 | brelse(sbi->s_vhbh); | 254 | kfree(sbi->s_vhdr); |
255 | kfree(sbi->s_backup_vhdr); | ||
235 | unload_nls(sbi->nls); | 256 | unload_nls(sbi->nls); |
236 | kfree(sb->s_fs_info); | 257 | kfree(sb->s_fs_info); |
237 | sb->s_fs_info = NULL; | 258 | sb->s_fs_info = NULL; |
@@ -263,26 +284,31 @@ static int hfsplus_remount(struct super_block *sb, int *flags, char *data) | |||
263 | return 0; | 284 | return 0; |
264 | if (!(*flags & MS_RDONLY)) { | 285 | if (!(*flags & MS_RDONLY)) { |
265 | struct hfsplus_vh *vhdr = HFSPLUS_SB(sb)->s_vhdr; | 286 | struct hfsplus_vh *vhdr = HFSPLUS_SB(sb)->s_vhdr; |
266 | struct hfsplus_sb_info sbi; | 287 | int force = 0; |
267 | 288 | ||
268 | memset(&sbi, 0, sizeof(struct hfsplus_sb_info)); | 289 | if (!hfsplus_parse_options_remount(data, &force)) |
269 | sbi.nls = HFSPLUS_SB(sb)->nls; | ||
270 | if (!hfsplus_parse_options(data, &sbi)) | ||
271 | return -EINVAL; | 290 | return -EINVAL; |
272 | 291 | ||
273 | if (!(vhdr->attributes & cpu_to_be32(HFSPLUS_VOL_UNMNT))) { | 292 | if (!(vhdr->attributes & cpu_to_be32(HFSPLUS_VOL_UNMNT))) { |
274 | printk(KERN_WARNING "hfs: filesystem was not cleanly unmounted, " | 293 | printk(KERN_WARNING "hfs: filesystem was " |
275 | "running fsck.hfsplus is recommended. leaving read-only.\n"); | 294 | "not cleanly unmounted, " |
295 | "running fsck.hfsplus is recommended. " | ||
296 | "leaving read-only.\n"); | ||
276 | sb->s_flags |= MS_RDONLY; | 297 | sb->s_flags |= MS_RDONLY; |
277 | *flags |= MS_RDONLY; | 298 | *flags |= MS_RDONLY; |
278 | } else if (test_bit(HFSPLUS_SB_FORCE, &sbi.flags)) { | 299 | } else if (force) { |
279 | /* nothing */ | 300 | /* nothing */ |
280 | } else if (vhdr->attributes & cpu_to_be32(HFSPLUS_VOL_SOFTLOCK)) { | 301 | } else if (vhdr->attributes & |
281 | printk(KERN_WARNING "hfs: filesystem is marked locked, leaving read-only.\n"); | 302 | cpu_to_be32(HFSPLUS_VOL_SOFTLOCK)) { |
303 | printk(KERN_WARNING "hfs: filesystem is marked locked, " | ||
304 | "leaving read-only.\n"); | ||
282 | sb->s_flags |= MS_RDONLY; | 305 | sb->s_flags |= MS_RDONLY; |
283 | *flags |= MS_RDONLY; | 306 | *flags |= MS_RDONLY; |
284 | } else if (vhdr->attributes & cpu_to_be32(HFSPLUS_VOL_JOURNALED)) { | 307 | } else if (vhdr->attributes & |
285 | printk(KERN_WARNING "hfs: filesystem is marked journaled, leaving read-only.\n"); | 308 | cpu_to_be32(HFSPLUS_VOL_JOURNALED)) { |
309 | printk(KERN_WARNING "hfs: filesystem is " | ||
310 | "marked journaled, " | ||
311 | "leaving read-only.\n"); | ||
286 | sb->s_flags |= MS_RDONLY; | 312 | sb->s_flags |= MS_RDONLY; |
287 | *flags |= MS_RDONLY; | 313 | *flags |= MS_RDONLY; |
288 | } | 314 | } |
@@ -372,17 +398,22 @@ static int hfsplus_fill_super(struct super_block *sb, void *data, int silent) | |||
372 | sb->s_maxbytes = MAX_LFS_FILESIZE; | 398 | sb->s_maxbytes = MAX_LFS_FILESIZE; |
373 | 399 | ||
374 | if (!(vhdr->attributes & cpu_to_be32(HFSPLUS_VOL_UNMNT))) { | 400 | if (!(vhdr->attributes & cpu_to_be32(HFSPLUS_VOL_UNMNT))) { |
375 | printk(KERN_WARNING "hfs: Filesystem was not cleanly unmounted, " | 401 | printk(KERN_WARNING "hfs: Filesystem was " |
376 | "running fsck.hfsplus is recommended. mounting read-only.\n"); | 402 | "not cleanly unmounted, " |
403 | "running fsck.hfsplus is recommended. " | ||
404 | "mounting read-only.\n"); | ||
377 | sb->s_flags |= MS_RDONLY; | 405 | sb->s_flags |= MS_RDONLY; |
378 | } else if (test_and_clear_bit(HFSPLUS_SB_FORCE, &sbi->flags)) { | 406 | } else if (test_and_clear_bit(HFSPLUS_SB_FORCE, &sbi->flags)) { |
379 | /* nothing */ | 407 | /* nothing */ |
380 | } else if (vhdr->attributes & cpu_to_be32(HFSPLUS_VOL_SOFTLOCK)) { | 408 | } else if (vhdr->attributes & cpu_to_be32(HFSPLUS_VOL_SOFTLOCK)) { |
381 | printk(KERN_WARNING "hfs: Filesystem is marked locked, mounting read-only.\n"); | 409 | printk(KERN_WARNING "hfs: Filesystem is marked locked, mounting read-only.\n"); |
382 | sb->s_flags |= MS_RDONLY; | 410 | sb->s_flags |= MS_RDONLY; |
383 | } else if ((vhdr->attributes & cpu_to_be32(HFSPLUS_VOL_JOURNALED)) && !(sb->s_flags & MS_RDONLY)) { | 411 | } else if ((vhdr->attributes & cpu_to_be32(HFSPLUS_VOL_JOURNALED)) && |
384 | printk(KERN_WARNING "hfs: write access to a journaled filesystem is not supported, " | 412 | !(sb->s_flags & MS_RDONLY)) { |
385 | "use the force option at your own risk, mounting read-only.\n"); | 413 | printk(KERN_WARNING "hfs: write access to " |
414 | "a journaled filesystem is not supported, " | ||
415 | "use the force option at your own risk, " | ||
416 | "mounting read-only.\n"); | ||
386 | sb->s_flags |= MS_RDONLY; | 417 | sb->s_flags |= MS_RDONLY; |
387 | } | 418 | } |
388 | 419 | ||
@@ -449,19 +480,16 @@ static int hfsplus_fill_super(struct super_block *sb, void *data, int silent) | |||
449 | be32_add_cpu(&vhdr->write_count, 1); | 480 | be32_add_cpu(&vhdr->write_count, 1); |
450 | vhdr->attributes &= cpu_to_be32(~HFSPLUS_VOL_UNMNT); | 481 | vhdr->attributes &= cpu_to_be32(~HFSPLUS_VOL_UNMNT); |
451 | vhdr->attributes |= cpu_to_be32(HFSPLUS_VOL_INCNSTNT); | 482 | vhdr->attributes |= cpu_to_be32(HFSPLUS_VOL_INCNSTNT); |
452 | mark_buffer_dirty(sbi->s_vhbh); | 483 | hfsplus_sync_fs(sb, 1); |
453 | sync_dirty_buffer(sbi->s_vhbh); | ||
454 | 484 | ||
455 | if (!sbi->hidden_dir) { | 485 | if (!sbi->hidden_dir) { |
456 | printk(KERN_DEBUG "hfs: create hidden dir...\n"); | ||
457 | |||
458 | mutex_lock(&sbi->vh_mutex); | 486 | mutex_lock(&sbi->vh_mutex); |
459 | sbi->hidden_dir = hfsplus_new_inode(sb, S_IFDIR); | 487 | sbi->hidden_dir = hfsplus_new_inode(sb, S_IFDIR); |
460 | hfsplus_create_cat(sbi->hidden_dir->i_ino, sb->s_root->d_inode, | 488 | hfsplus_create_cat(sbi->hidden_dir->i_ino, sb->s_root->d_inode, |
461 | &str, sbi->hidden_dir); | 489 | &str, sbi->hidden_dir); |
462 | mutex_unlock(&sbi->vh_mutex); | 490 | mutex_unlock(&sbi->vh_mutex); |
463 | 491 | ||
464 | mark_inode_dirty(sbi->hidden_dir); | 492 | hfsplus_mark_inode_dirty(sbi->hidden_dir, HFSPLUS_I_CAT_DIRTY); |
465 | } | 493 | } |
466 | out: | 494 | out: |
467 | unload_nls(sbi->nls); | 495 | unload_nls(sbi->nls); |