include upstream ip1000a driver version 2.09f
[linux-2.4.git] / fs / udf / dir.c
1 /*
2  * dir.c
3  *
4  * PURPOSE
5  *  Directory handling routines for the OSTA-UDF(tm) filesystem.
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  *  (C) 1998-2001 Ben Fennema
19  *
20  * HISTORY
21  *
22  *  10/05/98 dgb  Split directory operations into it's own file
23  *                Implemented directory reads via do_udf_readdir
24  *  10/06/98      Made directory operations work!
25  *  11/17/98      Rewrote directory to support ICBTAG_FLAG_AD_LONG
26  *  11/25/98 blf  Rewrote directory handling (readdir+lookup) to support reading
27  *                across blocks.
28  *  12/12/98      Split out the lookup code to namei.c. bulk of directory
29  *                code now in directory.c:udf_fileident_read.
30  */
31
32 #include "udfdecl.h"
33
34 #include <linux/string.h>
35 #include <linux/errno.h>
36 #include <linux/mm.h>
37 #include <linux/slab.h>
38
39 #include "udf_i.h"
40 #include "udf_sb.h"
41
42 /* Prototypes for file operations */
43 static int udf_readdir(struct file *, void *, filldir_t);
44 static int do_udf_readdir(struct inode *, struct file *, filldir_t, void *);
45
46 /* readdir and lookup functions */
47
48 struct file_operations udf_dir_operations = {
49         read:                   generic_read_dir,
50         readdir:                udf_readdir,
51         ioctl:                  udf_ioctl,
52         fsync:                  udf_fsync_file,
53 };
54
55 /*
56  * udf_readdir
57  *
58  * PURPOSE
59  *      Read a directory entry.
60  *
61  * DESCRIPTION
62  *      Optional - sys_getdents() will return -ENOTDIR if this routine is not
63  *      available.
64  *
65  *      Refer to sys_getdents() in fs/readdir.c
66  *      sys_getdents() -> .
67  *
68  * PRE-CONDITIONS
69  *      filp                    Pointer to directory file.
70  *      buf                     Pointer to directory entry buffer.
71  *      filldir                 Pointer to filldir function.
72  *
73  * POST-CONDITIONS
74  *      <return>                >=0 on success.
75  *
76  * HISTORY
77  *      July 1, 1997 - Andrew E. Mileski
78  *      Written, tested, and released.
79  */
80
81 int udf_readdir(struct file *filp, void *dirent, filldir_t filldir)
82 {
83         struct inode *dir = filp->f_dentry->d_inode;
84         int result;
85
86         if ( filp->f_pos == 0 ) 
87         {
88                 if (filldir(dirent, ".", 1, filp->f_pos, dir->i_ino, DT_DIR) < 0)
89                         return 0;
90                 filp->f_pos ++;
91         }
92  
93         result = do_udf_readdir(dir, filp, filldir, dirent);
94         UPDATE_ATIME(dir);
95         return result;
96 }
97
98 static int 
99 do_udf_readdir(struct inode * dir, struct file *filp, filldir_t filldir, void *dirent)
100 {
101         struct udf_fileident_bh fibh;
102         struct fileIdentDesc *fi=NULL;
103         struct fileIdentDesc cfi;
104         int block, iblock;
105         loff_t nf_pos = filp->f_pos - 1;
106         int flen;
107         char fname[255];
108         char *nameptr;
109         uint16_t liu;
110         uint8_t lfi;
111         loff_t size = (udf_ext0_offset(dir) + dir->i_size) >> 2;
112         struct buffer_head * bh = NULL, * tmp, * bha[16];
113         lb_addr bloc, eloc;
114         uint32_t extoffset, elen, offset;
115         int i, num;
116         unsigned int dt_type;
117
118         if (nf_pos >= size)
119                 return 0;
120
121         if (nf_pos == 0)
122                 nf_pos = (udf_ext0_offset(dir) >> 2);
123
124         fibh.soffset = fibh.eoffset = (nf_pos & ((dir->i_sb->s_blocksize - 1) >> 2)) << 2;
125         if (inode_bmap(dir, nf_pos >> (dir->i_sb->s_blocksize_bits - 2),
126                 &bloc, &extoffset, &eloc, &elen, &offset, &bh) == (EXT_RECORDED_ALLOCATED >> 30))
127         {
128                 offset >>= dir->i_sb->s_blocksize_bits;
129                 block = udf_get_lb_pblock(dir->i_sb, eloc, offset);
130                 if ((++offset << dir->i_sb->s_blocksize_bits) < elen)
131                 {
132                         if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_SHORT)
133                                 extoffset -= sizeof(short_ad);
134                         else if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_LONG)
135                                 extoffset -= sizeof(long_ad);
136                 }
137                 else
138                         offset = 0;
139         }
140         else
141         {
142                 udf_release_data(bh);
143                 return -ENOENT;
144         }
145
146         if (!(fibh.sbh = fibh.ebh = udf_tread(dir->i_sb, block)))
147         {
148                 udf_release_data(bh);
149                 return -EIO;
150         }
151
152         if (!(offset & ((16 >> (dir->i_sb->s_blocksize_bits - 9))-1)))
153         {
154                 i = 16 >> (dir->i_sb->s_blocksize_bits - 9);
155                 if (i+offset > (elen >> dir->i_sb->s_blocksize_bits))
156                         i = (elen >> dir->i_sb->s_blocksize_bits)-offset;
157                 for (num=0; i>0; i--)
158                 {
159                         block = udf_get_lb_pblock(dir->i_sb, eloc, offset+i);
160                         tmp = udf_tgetblk(dir->i_sb, block);
161                         if (tmp && !buffer_uptodate(tmp) && !buffer_locked(tmp))
162                                 bha[num++] = tmp;
163                         else
164                                 brelse(tmp);
165                 }
166                 if (num)
167                 {
168                         ll_rw_block(READA, num, bha);
169                         for (i=0; i<num; i++)
170                                 brelse(bha[i]);
171                 }
172         }
173
174         while ( nf_pos < size )
175         {
176                 filp->f_pos = nf_pos + 1;
177
178                 fi = udf_fileident_read(dir, &nf_pos, &fibh, &cfi, &bloc, &extoffset, &eloc, &elen, &offset, &bh);
179
180                 if (!fi)
181                 {
182                         if (fibh.sbh != fibh.ebh)
183                                 udf_release_data(fibh.ebh);
184                         udf_release_data(fibh.sbh);
185                         udf_release_data(bh);
186                         return 0;
187                 }
188
189                 liu = le16_to_cpu(cfi.lengthOfImpUse);
190                 lfi = cfi.lengthFileIdent;
191
192                 if (fibh.sbh == fibh.ebh)
193                         nameptr = fi->fileIdent + liu;
194                 else
195                 {
196                         int poffset;    /* Unpaded ending offset */
197
198                         poffset = fibh.soffset + sizeof(struct fileIdentDesc) + liu + lfi;
199
200                         if (poffset >= lfi)
201                                 nameptr = (char *)(fibh.ebh->b_data + poffset - lfi);
202                         else
203                         {
204                                 nameptr = fname;
205                                 memcpy(nameptr, fi->fileIdent + liu, lfi - poffset);
206                                 memcpy(nameptr + lfi - poffset, fibh.ebh->b_data, poffset);
207                         }
208                 }
209
210                 if ( (cfi.fileCharacteristics & FID_FILE_CHAR_DELETED) != 0 )
211                 {
212                         if ( !UDF_QUERY_FLAG(dir->i_sb, UDF_FLAG_UNDELETE) )
213                                 continue;
214                 }
215                 
216                 if ( (cfi.fileCharacteristics & FID_FILE_CHAR_HIDDEN) != 0 )
217                 {
218                         if ( !UDF_QUERY_FLAG(dir->i_sb, UDF_FLAG_UNHIDE) )
219                                 continue;
220                 }
221
222                 if ( cfi.fileCharacteristics & FID_FILE_CHAR_PARENT )
223                 {
224                         iblock = udf_get_lb_pblock(dir->i_sb, UDF_I_LOCATION(filp->f_dentry->d_parent->d_inode), 0);
225                         flen = 2;
226                         memcpy(fname, "..", flen);
227                         dt_type = DT_DIR;
228                 }
229                 else
230                 {
231                         iblock = udf_get_lb_pblock(dir->i_sb, lelb_to_cpu(cfi.icb.extLocation), 0);
232                         flen = udf_get_filename(dir->i_sb, nameptr, fname, lfi);
233                         dt_type = DT_UNKNOWN;
234                 }
235
236                 if (flen)
237                 {
238                         if (filldir(dirent, fname, flen, filp->f_pos, iblock, dt_type) < 0)
239                         {
240                                 if (fibh.sbh != fibh.ebh)
241                                         udf_release_data(fibh.ebh);
242                                 udf_release_data(fibh.sbh);
243                                 udf_release_data(bh);
244                                 return 0;
245                         }
246                 }
247         } /* end while */
248
249         filp->f_pos = nf_pos + 1;
250
251         if (fibh.sbh != fibh.ebh)
252                 udf_release_data(fibh.ebh);
253         udf_release_data(fibh.sbh);
254         udf_release_data(bh);
255
256         return 0;
257 }