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