more changes on original files
[linux-2.4.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
21 #include <linux/fs.h>
22 #include <linux/string.h>
23
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 *
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 * 
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 *
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 *
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 *
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 }