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