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