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