diff options
Diffstat (limited to 'fs/udf/misc.c')
-rw-r--r-- | fs/udf/misc.c | 313 |
1 files changed, 313 insertions, 0 deletions
diff --git a/fs/udf/misc.c b/fs/udf/misc.c new file mode 100644 index 000000000000..fd321f9ace83 --- /dev/null +++ b/fs/udf/misc.c | |||
@@ -0,0 +1,313 @@ | |||
1 | /* | ||
2 | * misc.c | ||
3 | * | ||
4 | * PURPOSE | ||
5 | * Miscellaneous routines for the OSTA-UDF(tm) filesystem. | ||
6 | * | ||
7 | * CONTACTS | ||
8 | * E-mail regarding any portion of the Linux UDF file system should be | ||
9 | * directed to the development team mailing list (run by majordomo): | ||
10 | * linux_udf@hpesjro.fc.hp.com | ||
11 | * | ||
12 | * COPYRIGHT | ||
13 | * This file is distributed under the terms of the GNU General Public | ||
14 | * License (GPL). Copies of the GPL can be obtained from: | ||
15 | * ftp://prep.ai.mit.edu/pub/gnu/GPL | ||
16 | * Each contributing author retains all rights to their own work. | ||
17 | * | ||
18 | * (C) 1998 Dave Boynton | ||
19 | * (C) 1998-2004 Ben Fennema | ||
20 | * (C) 1999-2000 Stelias Computing Inc | ||
21 | * | ||
22 | * HISTORY | ||
23 | * | ||
24 | * 04/19/99 blf partial support for reading/writing specific EA's | ||
25 | */ | ||
26 | |||
27 | #include "udfdecl.h" | ||
28 | |||
29 | #include <linux/fs.h> | ||
30 | #include <linux/string.h> | ||
31 | #include <linux/udf_fs.h> | ||
32 | #include <linux/buffer_head.h> | ||
33 | |||
34 | #include "udf_i.h" | ||
35 | #include "udf_sb.h" | ||
36 | |||
37 | struct buffer_head * | ||
38 | udf_tgetblk(struct super_block *sb, int block) | ||
39 | { | ||
40 | if (UDF_QUERY_FLAG(sb, UDF_FLAG_VARCONV)) | ||
41 | return sb_getblk(sb, udf_fixed_to_variable(block)); | ||
42 | else | ||
43 | return sb_getblk(sb, block); | ||
44 | } | ||
45 | |||
46 | struct buffer_head * | ||
47 | udf_tread(struct super_block *sb, int block) | ||
48 | { | ||
49 | if (UDF_QUERY_FLAG(sb, UDF_FLAG_VARCONV)) | ||
50 | return sb_bread(sb, udf_fixed_to_variable(block)); | ||
51 | else | ||
52 | return sb_bread(sb, block); | ||
53 | } | ||
54 | |||
55 | struct genericFormat * | ||
56 | udf_add_extendedattr(struct inode * inode, uint32_t size, uint32_t type, | ||
57 | uint8_t loc) | ||
58 | { | ||
59 | uint8_t *ea = NULL, *ad = NULL; | ||
60 | int offset; | ||
61 | uint16_t crclen; | ||
62 | int i; | ||
63 | |||
64 | ea = UDF_I_DATA(inode); | ||
65 | if (UDF_I_LENEATTR(inode)) | ||
66 | ad = UDF_I_DATA(inode) + UDF_I_LENEATTR(inode); | ||
67 | else | ||
68 | { | ||
69 | ad = ea; | ||
70 | size += sizeof(struct extendedAttrHeaderDesc); | ||
71 | } | ||
72 | |||
73 | offset = inode->i_sb->s_blocksize - udf_file_entry_alloc_offset(inode) - | ||
74 | UDF_I_LENALLOC(inode); | ||
75 | |||
76 | /* TODO - Check for FreeEASpace */ | ||
77 | |||
78 | if (loc & 0x01 && offset >= size) | ||
79 | { | ||
80 | struct extendedAttrHeaderDesc *eahd; | ||
81 | eahd = (struct extendedAttrHeaderDesc *)ea; | ||
82 | |||
83 | if (UDF_I_LENALLOC(inode)) | ||
84 | { | ||
85 | memmove(&ad[size], ad, UDF_I_LENALLOC(inode)); | ||
86 | } | ||
87 | |||
88 | if (UDF_I_LENEATTR(inode)) | ||
89 | { | ||
90 | /* check checksum/crc */ | ||
91 | if (le16_to_cpu(eahd->descTag.tagIdent) != TAG_IDENT_EAHD || | ||
92 | le32_to_cpu(eahd->descTag.tagLocation) != UDF_I_LOCATION(inode).logicalBlockNum) | ||
93 | { | ||
94 | return NULL; | ||
95 | } | ||
96 | } | ||
97 | else | ||
98 | { | ||
99 | size -= sizeof(struct extendedAttrHeaderDesc); | ||
100 | UDF_I_LENEATTR(inode) += sizeof(struct extendedAttrHeaderDesc); | ||
101 | eahd->descTag.tagIdent = cpu_to_le16(TAG_IDENT_EAHD); | ||
102 | if (UDF_SB_UDFREV(inode->i_sb) >= 0x0200) | ||
103 | eahd->descTag.descVersion = cpu_to_le16(3); | ||
104 | else | ||
105 | eahd->descTag.descVersion = cpu_to_le16(2); | ||
106 | eahd->descTag.tagSerialNum = cpu_to_le16(UDF_SB_SERIALNUM(inode->i_sb)); | ||
107 | eahd->descTag.tagLocation = cpu_to_le32(UDF_I_LOCATION(inode).logicalBlockNum); | ||
108 | eahd->impAttrLocation = cpu_to_le32(0xFFFFFFFF); | ||
109 | eahd->appAttrLocation = cpu_to_le32(0xFFFFFFFF); | ||
110 | } | ||
111 | |||
112 | offset = UDF_I_LENEATTR(inode); | ||
113 | if (type < 2048) | ||
114 | { | ||
115 | if (le32_to_cpu(eahd->appAttrLocation) < UDF_I_LENEATTR(inode)) | ||
116 | { | ||
117 | uint32_t aal = le32_to_cpu(eahd->appAttrLocation); | ||
118 | memmove(&ea[offset - aal + size], | ||
119 | &ea[aal], offset - aal); | ||
120 | offset -= aal; | ||
121 | eahd->appAttrLocation = cpu_to_le32(aal + size); | ||
122 | } | ||
123 | if (le32_to_cpu(eahd->impAttrLocation) < UDF_I_LENEATTR(inode)) | ||
124 | { | ||
125 | uint32_t ial = le32_to_cpu(eahd->impAttrLocation); | ||
126 | memmove(&ea[offset - ial + size], | ||
127 | &ea[ial], offset - ial); | ||
128 | offset -= ial; | ||
129 | eahd->impAttrLocation = cpu_to_le32(ial + size); | ||
130 | } | ||
131 | } | ||
132 | else if (type < 65536) | ||
133 | { | ||
134 | if (le32_to_cpu(eahd->appAttrLocation) < UDF_I_LENEATTR(inode)) | ||
135 | { | ||
136 | uint32_t aal = le32_to_cpu(eahd->appAttrLocation); | ||
137 | memmove(&ea[offset - aal + size], | ||
138 | &ea[aal], offset - aal); | ||
139 | offset -= aal; | ||
140 | eahd->appAttrLocation = cpu_to_le32(aal + size); | ||
141 | } | ||
142 | } | ||
143 | /* rewrite CRC + checksum of eahd */ | ||
144 | crclen = sizeof(struct extendedAttrHeaderDesc) - sizeof(tag); | ||
145 | eahd->descTag.descCRCLength = cpu_to_le16(crclen); | ||
146 | eahd->descTag.descCRC = cpu_to_le16(udf_crc((char *)eahd + sizeof(tag), crclen, 0)); | ||
147 | eahd->descTag.tagChecksum = 0; | ||
148 | for (i=0; i<16; i++) | ||
149 | if (i != 4) | ||
150 | eahd->descTag.tagChecksum += ((uint8_t *)&(eahd->descTag))[i]; | ||
151 | UDF_I_LENEATTR(inode) += size; | ||
152 | return (struct genericFormat *)&ea[offset]; | ||
153 | } | ||
154 | if (loc & 0x02) | ||
155 | { | ||
156 | } | ||
157 | return NULL; | ||
158 | } | ||
159 | |||
160 | struct genericFormat * | ||
161 | udf_get_extendedattr(struct inode *inode, uint32_t type, uint8_t subtype) | ||
162 | { | ||
163 | struct genericFormat *gaf; | ||
164 | uint8_t *ea = NULL; | ||
165 | uint32_t offset; | ||
166 | |||
167 | ea = UDF_I_DATA(inode); | ||
168 | |||
169 | if (UDF_I_LENEATTR(inode)) | ||
170 | { | ||
171 | struct extendedAttrHeaderDesc *eahd; | ||
172 | eahd = (struct extendedAttrHeaderDesc *)ea; | ||
173 | |||
174 | /* check checksum/crc */ | ||
175 | if (le16_to_cpu(eahd->descTag.tagIdent) != TAG_IDENT_EAHD || | ||
176 | le32_to_cpu(eahd->descTag.tagLocation) != UDF_I_LOCATION(inode).logicalBlockNum) | ||
177 | { | ||
178 | return NULL; | ||
179 | } | ||
180 | |||
181 | if (type < 2048) | ||
182 | offset = sizeof(struct extendedAttrHeaderDesc); | ||
183 | else if (type < 65536) | ||
184 | offset = le32_to_cpu(eahd->impAttrLocation); | ||
185 | else | ||
186 | offset = le32_to_cpu(eahd->appAttrLocation); | ||
187 | |||
188 | while (offset < UDF_I_LENEATTR(inode)) | ||
189 | { | ||
190 | gaf = (struct genericFormat *)&ea[offset]; | ||
191 | if (le32_to_cpu(gaf->attrType) == type && gaf->attrSubtype == subtype) | ||
192 | return gaf; | ||
193 | else | ||
194 | offset += le32_to_cpu(gaf->attrLength); | ||
195 | } | ||
196 | } | ||
197 | return NULL; | ||
198 | } | ||
199 | |||
200 | /* | ||
201 | * udf_read_tagged | ||
202 | * | ||
203 | * PURPOSE | ||
204 | * Read the first block of a tagged descriptor. | ||
205 | * | ||
206 | * HISTORY | ||
207 | * July 1, 1997 - Andrew E. Mileski | ||
208 | * Written, tested, and released. | ||
209 | */ | ||
210 | struct buffer_head * | ||
211 | udf_read_tagged(struct super_block *sb, uint32_t block, uint32_t location, uint16_t *ident) | ||
212 | { | ||
213 | tag *tag_p; | ||
214 | struct buffer_head *bh = NULL; | ||
215 | register uint8_t checksum; | ||
216 | register int i; | ||
217 | |||
218 | /* Read the block */ | ||
219 | if (block == 0xFFFFFFFF) | ||
220 | return NULL; | ||
221 | |||
222 | bh = udf_tread(sb, block + UDF_SB_SESSION(sb)); | ||
223 | if (!bh) | ||
224 | { | ||
225 | udf_debug("block=%d, location=%d: read failed\n", block + UDF_SB_SESSION(sb), location); | ||
226 | return NULL; | ||
227 | } | ||
228 | |||
229 | tag_p = (tag *)(bh->b_data); | ||
230 | |||
231 | *ident = le16_to_cpu(tag_p->tagIdent); | ||
232 | |||
233 | if ( location != le32_to_cpu(tag_p->tagLocation) ) | ||
234 | { | ||
235 | udf_debug("location mismatch block %u, tag %u != %u\n", | ||
236 | block + UDF_SB_SESSION(sb), le32_to_cpu(tag_p->tagLocation), location); | ||
237 | goto error_out; | ||
238 | } | ||
239 | |||
240 | /* Verify the tag checksum */ | ||
241 | checksum = 0U; | ||
242 | for (i = 0; i < 4; i++) | ||
243 | checksum += (uint8_t)(bh->b_data[i]); | ||
244 | for (i = 5; i < 16; i++) | ||
245 | checksum += (uint8_t)(bh->b_data[i]); | ||
246 | if (checksum != tag_p->tagChecksum) { | ||
247 | printk(KERN_ERR "udf: tag checksum failed block %d\n", block); | ||
248 | goto error_out; | ||
249 | } | ||
250 | |||
251 | /* Verify the tag version */ | ||
252 | if (le16_to_cpu(tag_p->descVersion) != 0x0002U && | ||
253 | le16_to_cpu(tag_p->descVersion) != 0x0003U) | ||
254 | { | ||
255 | udf_debug("tag version 0x%04x != 0x0002 || 0x0003 block %d\n", | ||
256 | le16_to_cpu(tag_p->descVersion), block); | ||
257 | goto error_out; | ||
258 | } | ||
259 | |||
260 | /* Verify the descriptor CRC */ | ||
261 | if (le16_to_cpu(tag_p->descCRCLength) + sizeof(tag) > sb->s_blocksize || | ||
262 | le16_to_cpu(tag_p->descCRC) == udf_crc(bh->b_data + sizeof(tag), | ||
263 | le16_to_cpu(tag_p->descCRCLength), 0)) | ||
264 | { | ||
265 | return bh; | ||
266 | } | ||
267 | udf_debug("Crc failure block %d: crc = %d, crclen = %d\n", | ||
268 | block + UDF_SB_SESSION(sb), le16_to_cpu(tag_p->descCRC), le16_to_cpu(tag_p->descCRCLength)); | ||
269 | |||
270 | error_out: | ||
271 | brelse(bh); | ||
272 | return NULL; | ||
273 | } | ||
274 | |||
275 | struct buffer_head * | ||
276 | udf_read_ptagged(struct super_block *sb, kernel_lb_addr loc, uint32_t offset, uint16_t *ident) | ||
277 | { | ||
278 | return udf_read_tagged(sb, udf_get_lb_pblock(sb, loc, offset), | ||
279 | loc.logicalBlockNum + offset, ident); | ||
280 | } | ||
281 | |||
282 | void udf_release_data(struct buffer_head *bh) | ||
283 | { | ||
284 | if (bh) | ||
285 | brelse(bh); | ||
286 | } | ||
287 | |||
288 | void udf_update_tag(char *data, int length) | ||
289 | { | ||
290 | tag *tptr = (tag *)data; | ||
291 | int i; | ||
292 | |||
293 | length -= sizeof(tag); | ||
294 | |||
295 | tptr->tagChecksum = 0; | ||
296 | tptr->descCRCLength = cpu_to_le16(length); | ||
297 | tptr->descCRC = cpu_to_le16(udf_crc(data + sizeof(tag), length, 0)); | ||
298 | |||
299 | for (i=0; i<16; i++) | ||
300 | if (i != 4) | ||
301 | tptr->tagChecksum += (uint8_t)(data[i]); | ||
302 | } | ||
303 | |||
304 | void udf_new_tag(char *data, uint16_t ident, uint16_t version, uint16_t snum, | ||
305 | uint32_t loc, int length) | ||
306 | { | ||
307 | tag *tptr = (tag *)data; | ||
308 | tptr->tagIdent = cpu_to_le16(ident); | ||
309 | tptr->descVersion = cpu_to_le16(version); | ||
310 | tptr->tagSerialNum = cpu_to_le16(snum); | ||
311 | tptr->tagLocation = cpu_to_le32(loc); | ||
312 | udf_update_tag(data, length); | ||
313 | } | ||