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-2001 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
33#include "udf_i.h"
34#include "udf_sb.h"
35
36uint32_t
37udf64_low32(uint64_t indat)
38{
39	return indat & 0x00000000FFFFFFFFULL;
40}
41
42uint32_t
43udf64_high32(uint64_t indat)
44{
45	return indat >> 32;
46}
47
48extern struct buffer_head *
49udf_tgetblk(struct super_block *sb, int block)
50{
51	if (UDF_QUERY_FLAG(sb, UDF_FLAG_VARCONV))
52		return sb_getblk(sb, udf_fixed_to_variable(block));
53	else
54		return sb_getblk(sb, block);
55}
56
57extern struct buffer_head *
58udf_tread(struct super_block *sb, int block)
59{
60	if (UDF_QUERY_FLAG(sb, UDF_FLAG_VARCONV))
61		return sb_bread(sb, udf_fixed_to_variable(block));
62	else
63		return sb_bread(sb, block);
64}
65
66extern struct genericFormat *
67udf_add_extendedattr(struct inode * inode, uint32_t size, uint32_t type,
68	uint8_t loc, struct buffer_head **bh)
69{
70	uint8_t *ea = NULL, *ad = NULL;
71	long_ad eaicb;
72	int offset;
73
74	*bh = udf_tread(inode->i_sb, inode->i_ino);
75
76	if (UDF_I_EXTENDED_FE(inode) == 0)
77	{
78		struct fileEntry *fe;
79
80		fe = (struct fileEntry *)(*bh)->b_data;
81		eaicb = lela_to_cpu(fe->extendedAttrICB);
82		offset = sizeof(struct fileEntry);
83	}
84	else
85	{
86		struct extendedFileEntry *efe;
87
88		efe = (struct extendedFileEntry *)(*bh)->b_data;
89		eaicb = lela_to_cpu(efe->extendedAttrICB);
90		offset = sizeof(struct extendedFileEntry);
91	}
92
93	ea = &(*bh)->b_data[offset];
94	if (UDF_I_LENEATTR(inode))
95		offset += UDF_I_LENEATTR(inode);
96	else
97		size += sizeof(struct extendedAttrHeaderDesc);
98
99	ad = &(*bh)->b_data[offset];
100	if (UDF_I_LENALLOC(inode))
101		offset += UDF_I_LENALLOC(inode);
102
103	offset = inode->i_sb->s_blocksize - offset;
104
105	/* TODO - Check for FreeEASpace */
106
107	if (loc & 0x01 && offset >= size)
108	{
109		struct extendedAttrHeaderDesc *eahd;
110		eahd = (struct extendedAttrHeaderDesc *)ea;
111
112		if (UDF_I_LENALLOC(inode))
113		{
114			memmove(&ad[size], ad, UDF_I_LENALLOC(inode));
115		}
116
117		if (UDF_I_LENEATTR(inode))
118		{
119			/* check checksum/crc */
120			if (le16_to_cpu(eahd->descTag.tagIdent) != TAG_IDENT_EAHD ||
121				le32_to_cpu(eahd->descTag.tagLocation) != UDF_I_LOCATION(inode).logicalBlockNum)
122			{
123				udf_release_data(*bh);
124				return NULL;
125			}
126		}
127		else
128		{
129			size -= sizeof(struct extendedAttrHeaderDesc);
130			UDF_I_LENEATTR(inode) += sizeof(struct extendedAttrHeaderDesc);
131			eahd->descTag.tagIdent = cpu_to_le16(TAG_IDENT_EAHD);
132			eahd->descTag.descVersion = cpu_to_le16(2);
133			eahd->descTag.tagSerialNum = cpu_to_le16(1);
134			eahd->descTag.tagLocation = cpu_to_le32(UDF_I_LOCATION(inode).logicalBlockNum);
135			eahd->impAttrLocation = cpu_to_le32(0xFFFFFFFF);
136			eahd->appAttrLocation = cpu_to_le32(0xFFFFFFFF);
137		}
138
139		offset = UDF_I_LENEATTR(inode);
140		if (type < 2048)
141		{
142			if (le32_to_cpu(eahd->appAttrLocation) < UDF_I_LENEATTR(inode))
143			{
144				uint32_t aal = le32_to_cpu(eahd->appAttrLocation);
145				memmove(&ea[offset - aal + size],
146					&ea[aal], offset - aal);
147				offset -= aal;
148				eahd->appAttrLocation = cpu_to_le32(aal + size);
149			}
150			if (le32_to_cpu(eahd->impAttrLocation) < UDF_I_LENEATTR(inode))
151			{
152				uint32_t ial = le32_to_cpu(eahd->impAttrLocation);
153				memmove(&ea[offset - ial + size],
154					&ea[ial], offset - ial);
155				offset -= ial;
156				eahd->impAttrLocation = cpu_to_le32(ial + size);
157			}
158		}
159		else if (type < 65536)
160		{
161			if (le32_to_cpu(eahd->appAttrLocation) < UDF_I_LENEATTR(inode))
162			{
163				uint32_t aal = le32_to_cpu(eahd->appAttrLocation);
164				memmove(&ea[offset - aal + size],
165					&ea[aal], offset - aal);
166				offset -= aal;
167				eahd->appAttrLocation = cpu_to_le32(aal + size);
168			}
169		}
170		/* rewrite CRC + checksum of eahd */
171		UDF_I_LENEATTR(inode) += size;
172		return (struct genericFormat *)&ea[offset];
173	}
174	if (loc & 0x02)
175	{
176	}
177	udf_release_data(*bh);
178	return NULL;
179}
180
181extern struct genericFormat *
182udf_get_extendedattr(struct inode * inode, uint32_t type, uint8_t subtype,
183	struct buffer_head **bh)
184{
185	struct genericFormat *gaf;
186	uint8_t *ea = NULL;
187	long_ad eaicb;
188	uint32_t offset;
189
190	*bh = udf_tread(inode->i_sb, inode->i_ino);
191
192	if (UDF_I_EXTENDED_FE(inode) == 0)
193	{
194		struct fileEntry *fe;
195
196		fe = (struct fileEntry *)(*bh)->b_data;
197		eaicb = lela_to_cpu(fe->extendedAttrICB);
198		if (UDF_I_LENEATTR(inode))
199			ea = fe->extendedAttr;
200	}
201	else
202	{
203		struct extendedFileEntry *efe;
204
205		efe = (struct extendedFileEntry *)(*bh)->b_data;
206		eaicb = lela_to_cpu(efe->extendedAttrICB);
207		if (UDF_I_LENEATTR(inode))
208			ea = efe->extendedAttr;
209	}
210
211	if (UDF_I_LENEATTR(inode))
212	{
213		struct extendedAttrHeaderDesc *eahd;
214		eahd = (struct extendedAttrHeaderDesc *)ea;
215
216		/* check checksum/crc */
217		if (le16_to_cpu(eahd->descTag.tagIdent) != TAG_IDENT_EAHD ||
218			le32_to_cpu(eahd->descTag.tagLocation) != UDF_I_LOCATION(inode).logicalBlockNum)
219		{
220			udf_release_data(*bh);
221			return NULL;
222		}
223
224		if (type < 2048)
225			offset = sizeof(struct extendedAttrHeaderDesc);
226		else if (type < 65536)
227			offset = le32_to_cpu(eahd->impAttrLocation);
228		else
229			offset = le32_to_cpu(eahd->appAttrLocation);
230
231		while (offset < UDF_I_LENEATTR(inode))
232		{
233			gaf = (struct genericFormat *)&ea[offset];
234			if (le32_to_cpu(gaf->attrType) == type && gaf->attrSubtype == subtype)
235				return gaf;
236			else
237				offset += le32_to_cpu(gaf->attrLength);
238		}
239	}
240
241	udf_release_data(*bh);
242	if (eaicb.extLength)
243	{
244		/* TODO */
245	}
246	return NULL;
247}
248
249/*
250 * udf_read_tagged
251 *
252 * PURPOSE
253 *	Read the first block of a tagged descriptor.
254 *
255 * HISTORY
256 *	July 1, 1997 - Andrew E. Mileski
257 *	Written, tested, and released.
258 */
259extern struct buffer_head *
260udf_read_tagged(struct super_block *sb, uint32_t block, uint32_t location, uint16_t *ident)
261{
262	tag *tag_p;
263	struct buffer_head *bh = NULL;
264	register uint8_t checksum;
265	register int i;
266
267	/* Read the block */
268	if (block == 0xFFFFFFFF)
269		return NULL;
270
271	bh = udf_tread(sb, block);
272	if (!bh)
273	{
274		udf_debug("block=%d, location=%d: read failed\n", block, location);
275		return NULL;
276	}
277
278	tag_p = (tag *)(bh->b_data);
279
280	*ident = le16_to_cpu(tag_p->tagIdent);
281
282	if ( location != le32_to_cpu(tag_p->tagLocation) )
283	{
284		udf_debug("location mismatch block %u, tag %u != %u\n",
285			block, le32_to_cpu(tag_p->tagLocation), location);
286		goto error_out;
287	}
288
289	/* Verify the tag checksum */
290	checksum = 0U;
291	for (i = 0; i < 4; i++)
292		checksum += (uint8_t)(bh->b_data[i]);
293	for (i = 5; i < 16; i++)
294		checksum += (uint8_t)(bh->b_data[i]);
295	if (checksum != tag_p->tagChecksum) {
296		printk(KERN_ERR "udf: tag checksum failed block %d\n", block);
297		goto error_out;
298	}
299
300	/* Verify the tag version */
301	if (le16_to_cpu(tag_p->descVersion) != 0x0002U &&
302		le16_to_cpu(tag_p->descVersion) != 0x0003U)
303	{
304		udf_debug("tag version 0x%04x != 0x0002 || 0x0003 block %d\n",
305			le16_to_cpu(tag_p->descVersion), block);
306		goto error_out;
307	}
308
309	/* Verify the descriptor CRC */
310	if (le16_to_cpu(tag_p->descCRCLength) + sizeof(tag) > sb->s_blocksize ||
311		le16_to_cpu(tag_p->descCRC) == udf_crc(bh->b_data + sizeof(tag),
312			le16_to_cpu(tag_p->descCRCLength), 0))
313	{
314		return bh;
315	}
316	udf_debug("Crc failure block %d: crc = %d, crclen = %d\n",
317		block, le16_to_cpu(tag_p->descCRC), le16_to_cpu(tag_p->descCRCLength));
318
319error_out:
320	brelse(bh);
321	return NULL;
322}
323
324extern struct buffer_head *
325udf_read_ptagged(struct super_block *sb, lb_addr loc, uint32_t offset, uint16_t *ident)
326{
327	return udf_read_tagged(sb, udf_get_lb_pblock(sb, loc, offset),
328		loc.logicalBlockNum + offset, ident);
329}
330
331void udf_release_data(struct buffer_head *bh)
332{
333	if (bh)
334		brelse(bh);
335}
336
337void udf_update_tag(char *data, int length)
338{
339	tag *tptr = (tag *)data;
340	int i;
341
342	length -= sizeof(tag);
343
344	tptr->tagChecksum = 0;
345	tptr->descCRCLength = le16_to_cpu(length);
346	tptr->descCRC = le16_to_cpu(udf_crc(data + sizeof(tag), length, 0));
347
348	for (i=0; i<16; i++)
349		if (i != 4)
350			tptr->tagChecksum += (uint8_t)(data[i]);
351}
352
353void udf_new_tag(char *data, uint16_t ident, uint16_t version, uint16_t snum,
354	uint32_t loc, int length)
355{
356	tag *tptr = (tag *)data;
357	tptr->tagIdent = le16_to_cpu(ident);
358	tptr->descVersion = le16_to_cpu(version);
359	tptr->tagSerialNum = le16_to_cpu(snum);
360	tptr->tagLocation = le32_to_cpu(loc);
361	udf_update_tag(data, length);
362}
363