Commit | Line | Data |
---|---|---|
1da177e4 LT |
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 | } |