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