Blame view

fs/hfsplus/wrapper.c 6.99 KB
b24413180   Greg Kroah-Hartman   License cleanup: ...
1
  // SPDX-License-Identifier: GPL-2.0
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  /*
   *  linux/fs/hfsplus/wrapper.c
   *
   * Copyright (C) 2001
   * Brad Boyer (flar@allandria.com)
   * (C) 2003 Ardis Technologies <roman@ardistech.com>
   *
   * Handling of HFS wrappers around HFS+ volumes
   */
  
  #include <linux/fs.h>
  #include <linux/blkdev.h>
  #include <linux/cdrom.h>
  #include <linux/genhd.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
16
17
18
19
20
21
22
23
24
25
26
  #include <asm/unaligned.h>
  
  #include "hfsplus_fs.h"
  #include "hfsplus_raw.h"
  
  struct hfsplus_wd {
  	u32 ablk_size;
  	u16 ablk_start;
  	u16 embed_start;
  	u16 embed_count;
  };
915ab236d   Fabian Frederick   fs/hfsplus/wrappe...
27
28
  /**
   * hfsplus_submit_bio - Perform block I/O
6596528e3   Seth Forshee   hfsplus: ensure b...
29
30
31
32
   * @sb: super block of volume for I/O
   * @sector: block to read or write, for blocks of HFSPLUS_SECTOR_SIZE bytes
   * @buf: buffer for I/O
   * @data: output pointer for location of requested data
67ed25961   Mike Christie   hfsplus: use bio ...
33
34
   * @op: direction of I/O
   * @op_flags: request op flags
6596528e3   Seth Forshee   hfsplus: ensure b...
35
36
37
38
39
40
41
42
43
44
45
46
47
   *
   * The unit of I/O is hfsplus_min_io_size(sb), which may be bigger than
   * HFSPLUS_SECTOR_SIZE, and @buf must be sized accordingly. On reads
   * @data will return a pointer to the start of the requested sector,
   * which may not be the same location as @buf.
   *
   * If @sector is not aligned to the bdev logical block size it will
   * be rounded down. For writes this means that @buf should contain data
   * that starts at the rounded-down address. As long as the data was
   * read using hfsplus_submit_bio() and the same buffer is used things
   * will work correctly.
   */
  int hfsplus_submit_bio(struct super_block *sb, sector_t sector,
67ed25961   Mike Christie   hfsplus: use bio ...
48
  		       void *buf, void **data, int op, int op_flags)
52399b171   Christoph Hellwig   hfsplus: use raw ...
49
  {
52399b171   Christoph Hellwig   hfsplus: use raw ...
50
  	struct bio *bio;
50176ddef   Seth Forshee   hfsplus: add miss...
51
  	int ret = 0;
a6dc8c042   Janne Kalliomäki   hfsplus: fix over...
52
  	u64 io_size;
6596528e3   Seth Forshee   hfsplus: ensure b...
53
54
55
56
57
58
59
60
61
62
63
64
  	loff_t start;
  	int offset;
  
  	/*
  	 * Align sector to hardware sector size and find offset. We
  	 * assume that io_size is a power of two, which _should_
  	 * be true.
  	 */
  	io_size = hfsplus_min_io_size(sb);
  	start = (loff_t)sector << HFSPLUS_SECTOR_SHIFT;
  	offset = start & (io_size - 1);
  	sector &= ~((io_size >> HFSPLUS_SECTOR_SHIFT) - 1);
52399b171   Christoph Hellwig   hfsplus: use raw ...
65
66
  
  	bio = bio_alloc(GFP_NOIO, 1);
4f024f379   Kent Overstreet   block: Abstract o...
67
  	bio->bi_iter.bi_sector = sector;
74d46992e   Christoph Hellwig   block: replace bi...
68
  	bio_set_dev(bio, sb->s_bdev);
67ed25961   Mike Christie   hfsplus: use bio ...
69
  	bio_set_op_attrs(bio, op, op_flags);
52399b171   Christoph Hellwig   hfsplus: use raw ...
70

67ed25961   Mike Christie   hfsplus: use bio ...
71
  	if (op != WRITE && data)
6596528e3   Seth Forshee   hfsplus: ensure b...
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
  		*data = (u8 *)buf + offset;
  
  	while (io_size > 0) {
  		unsigned int page_offset = offset_in_page(buf);
  		unsigned int len = min_t(unsigned int, PAGE_SIZE - page_offset,
  					 io_size);
  
  		ret = bio_add_page(bio, virt_to_page(buf), len, page_offset);
  		if (ret != len) {
  			ret = -EIO;
  			goto out;
  		}
  		io_size -= len;
  		buf = (u8 *)buf + len;
  	}
52399b171   Christoph Hellwig   hfsplus: use raw ...
87

4e49ea4a3   Mike Christie   block/fs/drivers:...
88
  	ret = submit_bio_wait(bio);
6596528e3   Seth Forshee   hfsplus: ensure b...
89
  out:
50176ddef   Seth Forshee   hfsplus: add miss...
90
  	bio_put(bio);
6596528e3   Seth Forshee   hfsplus: ensure b...
91
  	return ret < 0 ? ret : 0;
52399b171   Christoph Hellwig   hfsplus: use raw ...
92
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
93
94
95
96
  static int hfsplus_read_mdb(void *bufptr, struct hfsplus_wd *wd)
  {
  	u32 extent;
  	u16 attrib;
2179d372d   David Elliott   [PATCH] hfs: add ...
97
  	__be16 sig;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
98

2179d372d   David Elliott   [PATCH] hfs: add ...
99
100
101
  	sig = *(__be16 *)(bufptr + HFSP_WRAPOFF_EMBEDSIG);
  	if (sig != cpu_to_be16(HFSPLUS_VOLHEAD_SIG) &&
  	    sig != cpu_to_be16(HFSPLUS_VOLHEAD_SIGX))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
102
103
104
105
106
107
  		return 0;
  
  	attrib = be16_to_cpu(*(__be16 *)(bufptr + HFSP_WRAPOFF_ATTRIB));
  	if (!(attrib & HFSP_WRAP_ATTRIB_SLOCK) ||
  	   !(attrib & HFSP_WRAP_ATTRIB_SPARED))
  		return 0;
2753cc281   Anton Salikhmetov   hfsplus: over 80 ...
108
109
  	wd->ablk_size =
  		be32_to_cpu(*(__be32 *)(bufptr + HFSP_WRAPOFF_ABLKSIZE));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
110
111
112
113
  	if (wd->ablk_size < HFSPLUS_SECTOR_SIZE)
  		return 0;
  	if (wd->ablk_size % HFSPLUS_SECTOR_SIZE)
  		return 0;
2753cc281   Anton Salikhmetov   hfsplus: over 80 ...
114
115
  	wd->ablk_start =
  		be16_to_cpu(*(__be16 *)(bufptr + HFSP_WRAPOFF_ABLKSTART));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
116

8b3789e5d   Harvey Harrison   hfsplus: use get/...
117
  	extent = get_unaligned_be32(bufptr + HFSP_WRAPOFF_EMBEDEXT);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
118
119
120
121
122
123
124
125
126
  	wd->embed_start = (extent >> 16) & 0xFFFF;
  	wd->embed_count = extent & 0xFFFF;
  
  	return 1;
  }
  
  static int hfsplus_get_last_session(struct super_block *sb,
  				    sector_t *start, sector_t *size)
  {
f252fa33d   Christoph Hellwig   hfsplus: stop usi...
127
  	struct cdrom_device_info *cdi = disk_to_cdi(sb->s_bdev->bd_disk);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
128
129
130
  
  	/* default values */
  	*start = 0;
a3f223501   Fabian Frederick   hfsplus: atomical...
131
  	*size = i_size_read(sb->s_bdev->bd_inode) >> 9;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
132

dd73a01a3   Christoph Hellwig   hfsplus: fix HFSP...
133
  	if (HFSPLUS_SB(sb)->session >= 0) {
f252fa33d   Christoph Hellwig   hfsplus: stop usi...
134
135
136
137
  		struct cdrom_tocentry te;
  
  		if (!cdi)
  			return -EINVAL;
dd73a01a3   Christoph Hellwig   hfsplus: fix HFSP...
138
  		te.cdte_track = HFSPLUS_SB(sb)->session;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
139
  		te.cdte_format = CDROM_LBA;
f252fa33d   Christoph Hellwig   hfsplus: stop usi...
140
141
142
143
144
  		if (cdrom_read_tocentry(cdi, &te) ||
  		    (te.cdte_ctrl & CDROM_DATA_TRACK) != 4) {
  			pr_err("invalid session number or type of track
  ");
  			return -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
145
  		}
f252fa33d   Christoph Hellwig   hfsplus: stop usi...
146
147
148
149
150
151
152
  		*start = (sector_t)te.cdte_addr.lba << 2;
  	} else if (cdi) {
  		struct cdrom_multisession ms_info;
  
  		ms_info.addr_format = CDROM_LBA;
  		if (cdrom_multisession(cdi, &ms_info) == 0 && ms_info.xa_flag)
  			*start = (sector_t)ms_info.addr.lba << 2;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
153
  	}
f252fa33d   Christoph Hellwig   hfsplus: stop usi...
154

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
155
156
157
158
159
160
161
  	return 0;
  }
  
  /* Find the volume header and fill in some minimum bits in superblock */
  /* Takes in super block, returns true if good data read */
  int hfsplus_read_wrapper(struct super_block *sb)
  {
dd73a01a3   Christoph Hellwig   hfsplus: fix HFSP...
162
  	struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
163
164
165
  	struct hfsplus_wd wd;
  	sector_t part_start, part_size;
  	u32 blocksize;
52399b171   Christoph Hellwig   hfsplus: use raw ...
166
  	int error = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
167

52399b171   Christoph Hellwig   hfsplus: use raw ...
168
  	error = -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
169
170
  	blocksize = sb_min_blocksize(sb, HFSPLUS_SECTOR_SIZE);
  	if (!blocksize)
52399b171   Christoph Hellwig   hfsplus: use raw ...
171
  		goto out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
172
173
  
  	if (hfsplus_get_last_session(sb, &part_start, &part_size))
52399b171   Christoph Hellwig   hfsplus: use raw ...
174
  		goto out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
175

52399b171   Christoph Hellwig   hfsplus: use raw ...
176
  	error = -ENOMEM;
6596528e3   Seth Forshee   hfsplus: ensure b...
177
178
  	sbi->s_vhdr_buf = kmalloc(hfsplus_min_io_size(sb), GFP_KERNEL);
  	if (!sbi->s_vhdr_buf)
52399b171   Christoph Hellwig   hfsplus: use raw ...
179
  		goto out;
6596528e3   Seth Forshee   hfsplus: ensure b...
180
181
  	sbi->s_backup_vhdr_buf = kmalloc(hfsplus_min_io_size(sb), GFP_KERNEL);
  	if (!sbi->s_backup_vhdr_buf)
52399b171   Christoph Hellwig   hfsplus: use raw ...
182
183
184
  		goto out_free_vhdr;
  
  reread:
6596528e3   Seth Forshee   hfsplus: ensure b...
185
186
  	error = hfsplus_submit_bio(sb, part_start + HFSPLUS_VOLHEAD_SECTOR,
  				   sbi->s_vhdr_buf, (void **)&sbi->s_vhdr,
67ed25961   Mike Christie   hfsplus: use bio ...
187
  				   REQ_OP_READ, 0);
52399b171   Christoph Hellwig   hfsplus: use raw ...
188
189
190
191
192
193
194
195
196
197
198
199
  	if (error)
  		goto out_free_backup_vhdr;
  
  	error = -EINVAL;
  	switch (sbi->s_vhdr->signature) {
  	case cpu_to_be16(HFSPLUS_VOLHEAD_SIGX):
  		set_bit(HFSPLUS_SB_HFSX, &sbi->flags);
  		/*FALLTHRU*/
  	case cpu_to_be16(HFSPLUS_VOLHEAD_SIG):
  		break;
  	case cpu_to_be16(HFSP_WRAP_MAGIC):
  		if (!hfsplus_read_mdb(sbi->s_vhdr, &wd))
a1dbcef01   Chuck Ebbert   hfsplus: fix two ...
200
  			goto out_free_backup_vhdr;
52399b171   Christoph Hellwig   hfsplus: use raw ...
201
  		wd.ablk_size >>= HFSPLUS_SECTOR_SHIFT;
4ba2d5fdc   Christoph Hellwig   hfsplus: fix over...
202
203
204
  		part_start += (sector_t)wd.ablk_start +
  			       (sector_t)wd.embed_start * wd.ablk_size;
  		part_size = (sector_t)wd.embed_count * wd.ablk_size;
52399b171   Christoph Hellwig   hfsplus: use raw ...
205
206
207
208
209
  		goto reread;
  	default:
  		/*
  		 * Check for a partition block.
  		 *
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
210
211
212
  		 * (should do this only for cdrom/loop though)
  		 */
  		if (hfs_part_find(sb, &part_start, &part_size))
a1dbcef01   Chuck Ebbert   hfsplus: fix two ...
213
  			goto out_free_backup_vhdr;
52399b171   Christoph Hellwig   hfsplus: use raw ...
214
215
  		goto reread;
  	}
6596528e3   Seth Forshee   hfsplus: ensure b...
216
217
  	error = hfsplus_submit_bio(sb, part_start + part_size - 2,
  				   sbi->s_backup_vhdr_buf,
67ed25961   Mike Christie   hfsplus: use bio ...
218
219
  				   (void **)&sbi->s_backup_vhdr, REQ_OP_READ,
  				   0);
52399b171   Christoph Hellwig   hfsplus: use raw ...
220
221
222
223
224
  	if (error)
  		goto out_free_backup_vhdr;
  
  	error = -EINVAL;
  	if (sbi->s_backup_vhdr->signature != sbi->s_vhdr->signature) {
d61426732   Joe Perches   hfs/hfsplus: conv...
225
226
  		pr_warn("invalid secondary volume header
  ");
52399b171   Christoph Hellwig   hfsplus: use raw ...
227
  		goto out_free_backup_vhdr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
228
  	}
52399b171   Christoph Hellwig   hfsplus: use raw ...
229
  	blocksize = be32_to_cpu(sbi->s_vhdr->blocksize);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
230

52399b171   Christoph Hellwig   hfsplus: use raw ...
231
232
  	/*
  	 * Block size must be at least as large as a sector and a multiple of 2.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
233
  	 */
52399b171   Christoph Hellwig   hfsplus: use raw ...
234
235
  	if (blocksize < HFSPLUS_SECTOR_SIZE || ((blocksize - 1) & blocksize))
  		goto out_free_backup_vhdr;
dd73a01a3   Christoph Hellwig   hfsplus: fix HFSP...
236
  	sbi->alloc_blksz = blocksize;
297cc2720   Fabian Frederick   fs/hfsplus/wrappe...
237
  	sbi->alloc_blksz_shift = ilog2(blocksize);
915ab236d   Fabian Frederick   fs/hfsplus/wrappe...
238
  	blocksize = min_t(u32, sbi->alloc_blksz, PAGE_SIZE);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
239

52399b171   Christoph Hellwig   hfsplus: use raw ...
240
241
242
  	/*
  	 * Align block size to block offset.
  	 */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
243
244
245
246
  	while (part_start & ((blocksize >> HFSPLUS_SECTOR_SHIFT) - 1))
  		blocksize >>= 1;
  
  	if (sb_set_blocksize(sb, blocksize) != blocksize) {
d61426732   Joe Perches   hfs/hfsplus: conv...
247
248
  		pr_err("unable to set blocksize to %u!
  ", blocksize);
52399b171   Christoph Hellwig   hfsplus: use raw ...
249
  		goto out_free_backup_vhdr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
250
  	}
dd73a01a3   Christoph Hellwig   hfsplus: fix HFSP...
251
252
  	sbi->blockoffset =
  		part_start >> (sb->s_blocksize_bits - HFSPLUS_SECTOR_SHIFT);
52399b171   Christoph Hellwig   hfsplus: use raw ...
253
  	sbi->part_start = part_start;
dd73a01a3   Christoph Hellwig   hfsplus: fix HFSP...
254
255
  	sbi->sect_count = part_size;
  	sbi->fs_shift = sbi->alloc_blksz_shift - sb->s_blocksize_bits;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
256
  	return 0;
52399b171   Christoph Hellwig   hfsplus: use raw ...
257
258
  
  out_free_backup_vhdr:
f588c960f   Seth Forshee   hfsplus: Fix kfre...
259
  	kfree(sbi->s_backup_vhdr_buf);
52399b171   Christoph Hellwig   hfsplus: use raw ...
260
  out_free_vhdr:
f588c960f   Seth Forshee   hfsplus: Fix kfre...
261
  	kfree(sbi->s_vhdr_buf);
52399b171   Christoph Hellwig   hfsplus: use raw ...
262
263
  out:
  	return error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
264
  }