aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--fs/hfsplus/hfsplus_fs.h8
-rw-r--r--fs/hfsplus/super.c48
-rw-r--r--fs/hfsplus/wrapper.c163
3 files changed, 131 insertions, 88 deletions
diff --git a/fs/hfsplus/hfsplus_fs.h b/fs/hfsplus/hfsplus_fs.h
index f07aa640c27d..276ddb0fd0fd 100644
--- a/fs/hfsplus/hfsplus_fs.h
+++ b/fs/hfsplus/hfsplus_fs.h
@@ -107,8 +107,8 @@ struct hfsplus_vh;
107struct hfs_btree; 107struct hfs_btree;
108 108
109struct hfsplus_sb_info { 109struct hfsplus_sb_info {
110 struct buffer_head *s_vhbh;
111 struct hfsplus_vh *s_vhdr; 110 struct hfsplus_vh *s_vhdr;
111 struct hfsplus_vh *s_backup_vhdr;
112 struct hfs_btree *ext_tree; 112 struct hfs_btree *ext_tree;
113 struct hfs_btree *cat_tree; 113 struct hfs_btree *cat_tree;
114 struct hfs_btree *attr_tree; 114 struct hfs_btree *attr_tree;
@@ -118,7 +118,8 @@ struct hfsplus_sb_info {
118 118
119 /* Runtime variables */ 119 /* Runtime variables */
120 u32 blockoffset; 120 u32 blockoffset;
121 u32 sect_count; 121 sector_t part_start;
122 sector_t sect_count;
122 int fs_shift; 123 int fs_shift;
123 124
124 /* immutable data from the volume header */ 125 /* immutable data from the volume header */
@@ -385,8 +386,9 @@ int hfsplus_compare_dentry(struct dentry *dentry, struct qstr *s1, struct qstr *
385 386
386/* wrapper.c */ 387/* wrapper.c */
387int hfsplus_read_wrapper(struct super_block *); 388int hfsplus_read_wrapper(struct super_block *);
388
389int hfs_part_find(struct super_block *, sector_t *, sector_t *); 389int hfs_part_find(struct super_block *, sector_t *, sector_t *);
390int hfsplus_submit_bio(struct block_device *bdev, sector_t sector,
391 void *data, int rw);
390 392
391/* access macros */ 393/* access macros */
392static inline struct hfsplus_sb_info *HFSPLUS_SB(struct super_block *sb) 394static inline struct hfsplus_sb_info *HFSPLUS_SB(struct super_block *sb)
diff --git a/fs/hfsplus/super.c b/fs/hfsplus/super.c
index 6a2349058618..fe8f7bffbea5 100644
--- a/fs/hfsplus/super.c
+++ b/fs/hfsplus/super.c
@@ -157,45 +157,40 @@ int hfsplus_sync_fs(struct super_block *sb, int wait)
157{ 157{
158 struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb); 158 struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb);
159 struct hfsplus_vh *vhdr = sbi->s_vhdr; 159 struct hfsplus_vh *vhdr = sbi->s_vhdr;
160 int write_backup = 0;
161 int error, error2;
160 162
161 dprint(DBG_SUPER, "hfsplus_write_super\n"); 163 dprint(DBG_SUPER, "hfsplus_write_super\n");
162 164
163 mutex_lock(&sbi->vh_mutex);
164 mutex_lock(&sbi->alloc_mutex);
165 sb->s_dirt = 0; 165 sb->s_dirt = 0;
166 166
167 mutex_lock(&sbi->vh_mutex);
168 mutex_lock(&sbi->alloc_mutex);
167 vhdr->free_blocks = cpu_to_be32(sbi->free_blocks); 169 vhdr->free_blocks = cpu_to_be32(sbi->free_blocks);
168 vhdr->next_cnid = cpu_to_be32(sbi->next_cnid); 170 vhdr->next_cnid = cpu_to_be32(sbi->next_cnid);
169 vhdr->folder_count = cpu_to_be32(sbi->folder_count); 171 vhdr->folder_count = cpu_to_be32(sbi->folder_count);
170 vhdr->file_count = cpu_to_be32(sbi->file_count); 172 vhdr->file_count = cpu_to_be32(sbi->file_count);
171 173
172 mark_buffer_dirty(sbi->s_vhbh);
173 if (test_and_clear_bit(HFSPLUS_SB_WRITEBACKUP, &sbi->flags)) { 174 if (test_and_clear_bit(HFSPLUS_SB_WRITEBACKUP, &sbi->flags)) {
174 if (sbi->sect_count) { 175 memcpy(sbi->s_backup_vhdr, sbi->s_vhdr, sizeof(*sbi->s_vhdr));
175 struct buffer_head *bh; 176 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 } 177 }
178
179 error = hfsplus_submit_bio(sb->s_bdev,
180 sbi->part_start + HFSPLUS_VOLHEAD_SECTOR,
181 sbi->s_vhdr, WRITE_SYNC);
182 if (!write_backup)
183 goto out;
184
185 error2 = hfsplus_submit_bio(sb->s_bdev,
186 sbi->part_start + sbi->sect_count - 2,
187 sbi->s_backup_vhdr, WRITE_SYNC);
188 if (!error)
189 error2 = error;
190out:
196 mutex_unlock(&sbi->alloc_mutex); 191 mutex_unlock(&sbi->alloc_mutex);
197 mutex_unlock(&sbi->vh_mutex); 192 mutex_unlock(&sbi->vh_mutex);
198 return 0; 193 return error;
199} 194}
200 195
201static void hfsplus_write_super(struct super_block *sb) 196static void hfsplus_write_super(struct super_block *sb)
@@ -229,7 +224,8 @@ static void hfsplus_put_super(struct super_block *sb)
229 hfs_btree_close(sbi->ext_tree); 224 hfs_btree_close(sbi->ext_tree);
230 iput(sbi->alloc_file); 225 iput(sbi->alloc_file);
231 iput(sbi->hidden_dir); 226 iput(sbi->hidden_dir);
232 brelse(sbi->s_vhbh); 227 kfree(sbi->s_vhdr);
228 kfree(sbi->s_backup_vhdr);
233 unload_nls(sbi->nls); 229 unload_nls(sbi->nls);
234 kfree(sb->s_fs_info); 230 kfree(sb->s_fs_info);
235 sb->s_fs_info = NULL; 231 sb->s_fs_info = NULL;
diff --git a/fs/hfsplus/wrapper.c b/fs/hfsplus/wrapper.c
index 8972c20b3216..15e0eabb489e 100644
--- a/fs/hfsplus/wrapper.c
+++ b/fs/hfsplus/wrapper.c
@@ -24,6 +24,40 @@ struct hfsplus_wd {
24 u16 embed_count; 24 u16 embed_count;
25}; 25};
26 26
27static void hfsplus_end_io_sync(struct bio *bio, int err)
28{
29 if (err)
30 clear_bit(BIO_UPTODATE, &bio->bi_flags);
31 complete(bio->bi_private);
32}
33
34int hfsplus_submit_bio(struct block_device *bdev, sector_t sector,
35 void *data, int rw)
36{
37 DECLARE_COMPLETION_ONSTACK(wait);
38 struct bio *bio;
39
40 bio = bio_alloc(GFP_NOIO, 1);
41 bio->bi_sector = sector;
42 bio->bi_bdev = bdev;
43 bio->bi_end_io = hfsplus_end_io_sync;
44 bio->bi_private = &wait;
45
46 /*
47 * We always submit one sector at a time, so bio_add_page must not fail.
48 */
49 if (bio_add_page(bio, virt_to_page(data), HFSPLUS_SECTOR_SIZE,
50 offset_in_page(data)) != HFSPLUS_SECTOR_SIZE)
51 BUG();
52
53 submit_bio(rw, bio);
54 wait_for_completion(&wait);
55
56 if (!bio_flagged(bio, BIO_UPTODATE))
57 return -EIO;
58 return 0;
59}
60
27static int hfsplus_read_mdb(void *bufptr, struct hfsplus_wd *wd) 61static int hfsplus_read_mdb(void *bufptr, struct hfsplus_wd *wd)
28{ 62{
29 u32 extent; 63 u32 extent;
@@ -88,100 +122,111 @@ static int hfsplus_get_last_session(struct super_block *sb,
88int hfsplus_read_wrapper(struct super_block *sb) 122int hfsplus_read_wrapper(struct super_block *sb)
89{ 123{
90 struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb); 124 struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb);
91 struct buffer_head *bh;
92 struct hfsplus_vh *vhdr;
93 struct hfsplus_wd wd; 125 struct hfsplus_wd wd;
94 sector_t part_start, part_size; 126 sector_t part_start, part_size;
95 u32 blocksize; 127 u32 blocksize;
128 int error = 0;
96 129
130 error = -EINVAL;
97 blocksize = sb_min_blocksize(sb, HFSPLUS_SECTOR_SIZE); 131 blocksize = sb_min_blocksize(sb, HFSPLUS_SECTOR_SIZE);
98 if (!blocksize) 132 if (!blocksize)
99 return -EINVAL; 133 goto out;
100 134
101 if (hfsplus_get_last_session(sb, &part_start, &part_size)) 135 if (hfsplus_get_last_session(sb, &part_start, &part_size))
102 return -EINVAL; 136 goto out;
103 if ((u64)part_start + part_size > 0x100000000ULL) { 137 if ((u64)part_start + part_size > 0x100000000ULL) {
104 pr_err("hfs: volumes larger than 2TB are not supported yet\n"); 138 pr_err("hfs: volumes larger than 2TB are not supported yet\n");
105 return -EINVAL; 139 goto out;
106 } 140 }
107 while (1) {
108 bh = sb_bread512(sb, part_start + HFSPLUS_VOLHEAD_SECTOR, vhdr);
109 if (!bh)
110 return -EIO;
111
112 if (vhdr->signature == cpu_to_be16(HFSP_WRAP_MAGIC)) {
113 if (!hfsplus_read_mdb(vhdr, &wd))
114 goto error;
115 wd.ablk_size >>= HFSPLUS_SECTOR_SHIFT;
116 part_start += wd.ablk_start + wd.embed_start * wd.ablk_size;
117 part_size = wd.embed_count * wd.ablk_size;
118 brelse(bh);
119 bh = sb_bread512(sb, part_start + HFSPLUS_VOLHEAD_SECTOR, vhdr);
120 if (!bh)
121 return -EIO;
122 }
123 if (vhdr->signature == cpu_to_be16(HFSPLUS_VOLHEAD_SIG))
124 break;
125 if (vhdr->signature == cpu_to_be16(HFSPLUS_VOLHEAD_SIGX)) {
126 set_bit(HFSPLUS_SB_HFSX, &sbi->flags);
127 break;
128 }
129 brelse(bh);
130 141
131 /* check for a partition block 142 error = -ENOMEM;
143 sbi->s_vhdr = kmalloc(HFSPLUS_SECTOR_SIZE, GFP_KERNEL);
144 if (!sbi->s_vhdr)
145 goto out;
146 sbi->s_backup_vhdr = kmalloc(HFSPLUS_SECTOR_SIZE, GFP_KERNEL);
147 if (!sbi->s_backup_vhdr)
148 goto out_free_vhdr;
149
150reread:
151 error = hfsplus_submit_bio(sb->s_bdev,
152 part_start + HFSPLUS_VOLHEAD_SECTOR,
153 sbi->s_vhdr, READ);
154 if (error)
155 goto out_free_backup_vhdr;
156
157 error = -EINVAL;
158 switch (sbi->s_vhdr->signature) {
159 case cpu_to_be16(HFSPLUS_VOLHEAD_SIGX):
160 set_bit(HFSPLUS_SB_HFSX, &sbi->flags);
161 /*FALLTHRU*/
162 case cpu_to_be16(HFSPLUS_VOLHEAD_SIG):
163 break;
164 case cpu_to_be16(HFSP_WRAP_MAGIC):
165 if (!hfsplus_read_mdb(sbi->s_vhdr, &wd))
166 goto out;
167 wd.ablk_size >>= HFSPLUS_SECTOR_SHIFT;
168 part_start += wd.ablk_start + wd.embed_start * wd.ablk_size;
169 part_size = wd.embed_count * wd.ablk_size;
170 goto reread;
171 default:
172 /*
173 * Check for a partition block.
174 *
132 * (should do this only for cdrom/loop though) 175 * (should do this only for cdrom/loop though)
133 */ 176 */
134 if (hfs_part_find(sb, &part_start, &part_size)) 177 if (hfs_part_find(sb, &part_start, &part_size))
135 return -EINVAL; 178 goto out;
179 goto reread;
180 }
181
182 error = hfsplus_submit_bio(sb->s_bdev,
183 part_start + part_size - 2,
184 sbi->s_backup_vhdr, READ);
185 if (error)
186 goto out_free_backup_vhdr;
187
188 error = -EINVAL;
189 if (sbi->s_backup_vhdr->signature != sbi->s_vhdr->signature) {
190 printk(KERN_WARNING
191 "hfs: invalid secondary volume header\n");
192 goto out_free_backup_vhdr;
136 } 193 }
137 194
138 blocksize = be32_to_cpu(vhdr->blocksize); 195 blocksize = be32_to_cpu(sbi->s_vhdr->blocksize);
139 brelse(bh);
140 196
141 /* block size must be at least as large as a sector 197 /*
142 * and a multiple of 2 198 * Block size must be at least as large as a sector and a multiple of 2.
143 */ 199 */
144 if (blocksize < HFSPLUS_SECTOR_SIZE || 200 if (blocksize < HFSPLUS_SECTOR_SIZE || ((blocksize - 1) & blocksize))
145 ((blocksize - 1) & blocksize)) 201 goto out_free_backup_vhdr;
146 return -EINVAL;
147 sbi->alloc_blksz = blocksize; 202 sbi->alloc_blksz = blocksize;
148 sbi->alloc_blksz_shift = 0; 203 sbi->alloc_blksz_shift = 0;
149 while ((blocksize >>= 1) != 0) 204 while ((blocksize >>= 1) != 0)
150 sbi->alloc_blksz_shift++; 205 sbi->alloc_blksz_shift++;
151 blocksize = min(sbi->alloc_blksz, (u32)PAGE_SIZE); 206 blocksize = min(sbi->alloc_blksz, (u32)PAGE_SIZE);
152 207
153 /* align block size to block offset */ 208 /*
209 * Align block size to block offset.
210 */
154 while (part_start & ((blocksize >> HFSPLUS_SECTOR_SHIFT) - 1)) 211 while (part_start & ((blocksize >> HFSPLUS_SECTOR_SHIFT) - 1))
155 blocksize >>= 1; 212 blocksize >>= 1;
156 213
157 if (sb_set_blocksize(sb, blocksize) != blocksize) { 214 if (sb_set_blocksize(sb, blocksize) != blocksize) {
158 printk(KERN_ERR "hfs: unable to set blocksize to %u!\n", blocksize); 215 printk(KERN_ERR "hfs: unable to set blocksize to %u!\n", blocksize);
159 return -EINVAL; 216 goto out_free_backup_vhdr;
160 } 217 }
161 218
162 sbi->blockoffset = 219 sbi->blockoffset =
163 part_start >> (sb->s_blocksize_bits - HFSPLUS_SECTOR_SHIFT); 220 part_start >> (sb->s_blocksize_bits - HFSPLUS_SECTOR_SHIFT);
221 sbi->part_start = part_start;
164 sbi->sect_count = part_size; 222 sbi->sect_count = part_size;
165 sbi->fs_shift = sbi->alloc_blksz_shift - sb->s_blocksize_bits; 223 sbi->fs_shift = sbi->alloc_blksz_shift - sb->s_blocksize_bits;
166
167 bh = sb_bread512(sb, part_start + HFSPLUS_VOLHEAD_SECTOR, vhdr);
168 if (!bh)
169 return -EIO;
170
171 /* should still be the same... */
172 if (test_bit(HFSPLUS_SB_HFSX, &sbi->flags)) {
173 if (vhdr->signature != cpu_to_be16(HFSPLUS_VOLHEAD_SIGX))
174 goto error;
175 } else {
176 if (vhdr->signature != cpu_to_be16(HFSPLUS_VOLHEAD_SIG))
177 goto error;
178 }
179
180 sbi->s_vhbh = bh;
181 sbi->s_vhdr = vhdr;
182
183 return 0; 224 return 0;
184 error: 225
185 brelse(bh); 226out_free_backup_vhdr:
186 return -EINVAL; 227 kfree(sbi->s_backup_vhdr);
228out_free_vhdr:
229 kfree(sbi->s_vhdr);
230out:
231 return error;
187} 232}