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