xref: /openbmc/linux/fs/udf/directory.c (revision d5cb9783536a41df9f9cba5b0a1d78047ed787f7)
1 /*
2  * directory.c
3  *
4  * PURPOSE
5  *	Directory related functions
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 
19 #include "udfdecl.h"
20 #include "udf_i.h"
21 
22 #include <linux/fs.h>
23 #include <linux/string.h>
24 #include <linux/buffer_head.h>
25 
26 #if 0
27 static uint8_t *
28 udf_filead_read(struct inode *dir, uint8_t *tmpad, uint8_t ad_size,
29 		kernel_lb_addr fe_loc, int *pos, int *offset,
30 		struct buffer_head **bh, int *error)
31 {
32 	int loffset = *offset;
33 	int block;
34 	uint8_t *ad;
35 	int remainder;
36 
37 	*error = 0;
38 
39 	ad = (uint8_t *)(*bh)->b_data + *offset;
40 	*offset += ad_size;
41 
42 	if (!ad)
43 	{
44 		udf_release_data(*bh);
45 		*error = 1;
46 		return NULL;
47 	}
48 
49 	if (*offset == dir->i_sb->s_blocksize)
50 	{
51 		udf_release_data(*bh);
52 		block = udf_get_lb_pblock(dir->i_sb, fe_loc, ++*pos);
53 		if (!block)
54 			return NULL;
55 		if (!(*bh = udf_tread(dir->i_sb, block)))
56 			return NULL;
57 	}
58 	else if (*offset > dir->i_sb->s_blocksize)
59 	{
60 		ad = tmpad;
61 
62 		remainder = dir->i_sb->s_blocksize - loffset;
63 		memcpy((uint8_t *)ad, (*bh)->b_data + loffset, remainder);
64 
65 		udf_release_data(*bh);
66 		block = udf_get_lb_pblock(dir->i_sb, fe_loc, ++*pos);
67 		if (!block)
68 			return NULL;
69 		if (!((*bh) = udf_tread(dir->i_sb, block)))
70 			return NULL;
71 
72 		memcpy((uint8_t *)ad + remainder, (*bh)->b_data, ad_size - remainder);
73 		*offset = ad_size - remainder;
74 	}
75 	return ad;
76 }
77 #endif
78 
79 struct fileIdentDesc *
80 udf_fileident_read(struct inode *dir, loff_t *nf_pos,
81 	struct udf_fileident_bh *fibh,
82 	struct fileIdentDesc *cfi,
83 	kernel_lb_addr *bloc, uint32_t *extoffset,
84 	kernel_lb_addr *eloc, uint32_t *elen,
85 	uint32_t *offset, struct buffer_head **bh)
86 {
87 	struct fileIdentDesc *fi;
88 	int i, num, block;
89 	struct buffer_head * tmp, * bha[16];
90 
91 	fibh->soffset = fibh->eoffset;
92 
93 	if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_IN_ICB)
94 	{
95 		fi = udf_get_fileident(UDF_I_DATA(dir) -
96 			(UDF_I_EFE(dir) ?
97 				sizeof(struct extendedFileEntry) :
98 				sizeof(struct fileEntry)),
99 			dir->i_sb->s_blocksize, &(fibh->eoffset));
100 
101 		if (!fi)
102 			return NULL;
103 
104 		*nf_pos += ((fibh->eoffset - fibh->soffset) >> 2);
105 
106 		memcpy((uint8_t *)cfi, (uint8_t *)fi, sizeof(struct fileIdentDesc));
107 
108 		return fi;
109 	}
110 
111 	if (fibh->eoffset == dir->i_sb->s_blocksize)
112 	{
113 		int lextoffset = *extoffset;
114 
115 		if (udf_next_aext(dir, bloc, extoffset, eloc, elen, bh, 1) !=
116 			(EXT_RECORDED_ALLOCATED >> 30))
117 		{
118 			return NULL;
119 		}
120 
121 		block = udf_get_lb_pblock(dir->i_sb, *eloc, *offset);
122 
123 		(*offset) ++;
124 
125 		if ((*offset << dir->i_sb->s_blocksize_bits) >= *elen)
126 			*offset = 0;
127 		else
128 			*extoffset = lextoffset;
129 
130 		udf_release_data(fibh->sbh);
131 		if (!(fibh->sbh = fibh->ebh = udf_tread(dir->i_sb, block)))
132 			return NULL;
133 		fibh->soffset = fibh->eoffset = 0;
134 
135 		if (!(*offset & ((16 >> (dir->i_sb->s_blocksize_bits - 9))-1)))
136 		{
137 			i = 16 >> (dir->i_sb->s_blocksize_bits - 9);
138 			if (i+*offset > (*elen >> dir->i_sb->s_blocksize_bits))
139 				i = (*elen >> dir->i_sb->s_blocksize_bits)-*offset;
140 			for (num=0; i>0; i--)
141 			{
142 				block = udf_get_lb_pblock(dir->i_sb, *eloc, *offset+i);
143 				tmp = udf_tgetblk(dir->i_sb, block);
144 				if (tmp && !buffer_uptodate(tmp) && !buffer_locked(tmp))
145 					bha[num++] = tmp;
146 				else
147 					brelse(tmp);
148 			}
149 			if (num)
150 			{
151 				ll_rw_block(READA, num, bha);
152 				for (i=0; i<num; i++)
153 					brelse(bha[i]);
154 			}
155 		}
156 	}
157 	else if (fibh->sbh != fibh->ebh)
158 	{
159 		udf_release_data(fibh->sbh);
160 		fibh->sbh = fibh->ebh;
161 	}
162 
163 	fi = udf_get_fileident(fibh->sbh->b_data, dir->i_sb->s_blocksize,
164 		&(fibh->eoffset));
165 
166 	if (!fi)
167 		return NULL;
168 
169 	*nf_pos += ((fibh->eoffset - fibh->soffset) >> 2);
170 
171 	if (fibh->eoffset <= dir->i_sb->s_blocksize)
172 	{
173 		memcpy((uint8_t *)cfi, (uint8_t *)fi, sizeof(struct fileIdentDesc));
174 	}
175 	else if (fibh->eoffset > dir->i_sb->s_blocksize)
176 	{
177 		int lextoffset = *extoffset;
178 
179 		if (udf_next_aext(dir, bloc, extoffset, eloc, elen, bh, 1) !=
180 			(EXT_RECORDED_ALLOCATED >> 30))
181 		{
182 			return NULL;
183 		}
184 
185 		block = udf_get_lb_pblock(dir->i_sb, *eloc, *offset);
186 
187 		(*offset) ++;
188 
189 		if ((*offset << dir->i_sb->s_blocksize_bits) >= *elen)
190 			*offset = 0;
191 		else
192 			*extoffset = lextoffset;
193 
194 		fibh->soffset -= dir->i_sb->s_blocksize;
195 		fibh->eoffset -= dir->i_sb->s_blocksize;
196 
197 		if (!(fibh->ebh = udf_tread(dir->i_sb, block)))
198 			return NULL;
199 
200 		if (sizeof(struct fileIdentDesc) > - fibh->soffset)
201 		{
202 			int fi_len;
203 
204 			memcpy((uint8_t *)cfi, (uint8_t *)fi, - fibh->soffset);
205 			memcpy((uint8_t *)cfi - fibh->soffset, fibh->ebh->b_data,
206 				sizeof(struct fileIdentDesc) + fibh->soffset);
207 
208 			fi_len = (sizeof(struct fileIdentDesc) + cfi->lengthFileIdent +
209 				le16_to_cpu(cfi->lengthOfImpUse) + 3) & ~3;
210 
211 			*nf_pos += ((fi_len - (fibh->eoffset - fibh->soffset)) >> 2);
212 			fibh->eoffset = fibh->soffset + fi_len;
213 		}
214 		else
215 		{
216 			memcpy((uint8_t *)cfi, (uint8_t *)fi, sizeof(struct fileIdentDesc));
217 		}
218 	}
219 	return fi;
220 }
221 
222 struct fileIdentDesc *
223 udf_get_fileident(void * buffer, int bufsize, int * offset)
224 {
225 	struct fileIdentDesc *fi;
226 	int lengthThisIdent;
227 	uint8_t * ptr;
228 	int padlen;
229 
230 	if ( (!buffer) || (!offset) ) {
231 		udf_debug("invalidparms\n, buffer=%p, offset=%p\n", buffer, offset);
232 		return NULL;
233 	}
234 
235 	ptr = buffer;
236 
237 	if ( (*offset > 0) && (*offset < bufsize) ) {
238 		ptr += *offset;
239 	}
240 	fi=(struct fileIdentDesc *)ptr;
241 	if (le16_to_cpu(fi->descTag.tagIdent) != TAG_IDENT_FID)
242 	{
243 		udf_debug("0x%x != TAG_IDENT_FID\n",
244 			le16_to_cpu(fi->descTag.tagIdent));
245 		udf_debug("offset: %u sizeof: %lu bufsize: %u\n",
246 			*offset, (unsigned long)sizeof(struct fileIdentDesc), bufsize);
247 		return NULL;
248 	}
249 	if ( (*offset + sizeof(struct fileIdentDesc)) > bufsize )
250 	{
251 		lengthThisIdent = sizeof(struct fileIdentDesc);
252 	}
253 	else
254 		lengthThisIdent = sizeof(struct fileIdentDesc) +
255 			fi->lengthFileIdent + le16_to_cpu(fi->lengthOfImpUse);
256 
257 	/* we need to figure padding, too! */
258 	padlen = lengthThisIdent % UDF_NAME_PAD;
259 	if (padlen)
260 		lengthThisIdent += (UDF_NAME_PAD - padlen);
261 	*offset = *offset + lengthThisIdent;
262 
263 	return fi;
264 }
265 
266 #if 0
267 static extent_ad *
268 udf_get_fileextent(void * buffer, int bufsize, int * offset)
269 {
270 	extent_ad * ext;
271 	struct fileEntry *fe;
272 	uint8_t * ptr;
273 
274 	if ( (!buffer) || (!offset) )
275 	{
276 		printk(KERN_ERR "udf: udf_get_fileextent() invalidparms\n");
277 		return NULL;
278 	}
279 
280 	fe = (struct fileEntry *)buffer;
281 
282 	if ( le16_to_cpu(fe->descTag.tagIdent) != TAG_IDENT_FE )
283 	{
284 		udf_debug("0x%x != TAG_IDENT_FE\n",
285 			le16_to_cpu(fe->descTag.tagIdent));
286 		return NULL;
287 	}
288 
289 	ptr=(uint8_t *)(fe->extendedAttr) + le32_to_cpu(fe->lengthExtendedAttr);
290 
291 	if ( (*offset > 0) && (*offset < le32_to_cpu(fe->lengthAllocDescs)) )
292 	{
293 		ptr += *offset;
294 	}
295 
296 	ext = (extent_ad *)ptr;
297 
298 	*offset = *offset + sizeof(extent_ad);
299 	return ext;
300 }
301 #endif
302 
303 short_ad *
304 udf_get_fileshortad(uint8_t *ptr, int maxoffset, int *offset, int inc)
305 {
306 	short_ad *sa;
307 
308 	if ( (!ptr) || (!offset) )
309 	{
310 		printk(KERN_ERR "udf: udf_get_fileshortad() invalidparms\n");
311 		return NULL;
312 	}
313 
314 	if ( (*offset < 0) || ((*offset + sizeof(short_ad)) > maxoffset) )
315 		return NULL;
316 	else if ((sa = (short_ad *)ptr)->extLength == 0)
317 		return NULL;
318 
319 	if (inc)
320 		*offset += sizeof(short_ad);
321 	return sa;
322 }
323 
324 long_ad *
325 udf_get_filelongad(uint8_t *ptr, int maxoffset, int * offset, int inc)
326 {
327 	long_ad *la;
328 
329 	if ( (!ptr) || (!offset) )
330 	{
331 		printk(KERN_ERR "udf: udf_get_filelongad() invalidparms\n");
332 		return NULL;
333 	}
334 
335 	if ( (*offset < 0) || ((*offset + sizeof(long_ad)) > maxoffset) )
336 		return NULL;
337 	else if ((la = (long_ad *)ptr)->extLength == 0)
338 		return NULL;
339 
340 	if (inc)
341 		*offset += sizeof(long_ad);
342 	return la;
343 }
344