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