VServer 1.9.2 (patch-2.6.8.1-vs1.9.2.diff)
[linux-2.6.git] / fs / isofs / dir.c
1 /*
2  *  linux/fs/isofs/dir.c
3  *
4  *  (C) 1992, 1993, 1994  Eric Youngdale Modified for ISO 9660 filesystem.
5  *
6  *  (C) 1991  Linus Torvalds - minix filesystem
7  *
8  *  Steve Beynon                       : Missing last directory entries fixed
9  *  (stephen@askone.demon.co.uk)      : 21st June 1996
10  * 
11  *  isofs directory handling functions
12  */
13 #include <linux/errno.h>
14 #include <linux/fs.h>
15 #include <linux/iso_fs.h>
16 #include <linux/kernel.h>
17 #include <linux/stat.h>
18 #include <linux/string.h>
19 #include <linux/mm.h>
20 #include <linux/slab.h>
21 #include <linux/time.h>
22 #include <linux/config.h>
23 #include <linux/smp_lock.h>
24 #include <linux/buffer_head.h>
25
26 #include <asm/uaccess.h>
27
28 static int isofs_readdir(struct file *, void *, filldir_t);
29
30 struct file_operations isofs_dir_operations =
31 {
32         .read           = generic_read_dir,
33         .readdir        = isofs_readdir,
34 };
35
36 /*
37  * directories can handle most operations...
38  */
39 struct inode_operations isofs_dir_inode_operations =
40 {
41         .lookup         = isofs_lookup,
42 };
43
44 int isofs_name_translate(struct iso_directory_record *de, char *new, struct inode *inode)
45 {
46         char * old = de->name;
47         int len = de->name_len[0];
48         int i;
49                         
50         for (i = 0; i < len; i++) {
51                 unsigned char c = old[i];
52                 if (!c)
53                         break;
54
55                 if (c >= 'A' && c <= 'Z')
56                         c |= 0x20;      /* lower case */
57
58                 /* Drop trailing '.;1' (ISO 9660:1988 7.5.1 requires period) */
59                 if (c == '.' && i == len - 3 && old[i + 1] == ';' && old[i + 2] == '1')
60                         break;
61
62                 /* Drop trailing ';1' */
63                 if (c == ';' && i == len - 2 && old[i + 1] == '1')
64                         break;
65
66                 /* Convert remaining ';' to '.' */
67                 /* Also '/' to '.' (broken Acorn-generated ISO9660 images) */
68                 if (c == ';' || c == '/')
69                         c = '.';
70
71                 new[i] = c;
72         }
73         return i;
74 }
75
76 /* Acorn extensions written by Matthew Wilcox <willy@bofh.ai> 1998 */
77 int get_acorn_filename(struct iso_directory_record * de,
78                             char * retname, struct inode * inode)
79 {
80         int std;
81         unsigned char * chr;
82         int retnamlen = isofs_name_translate(de, retname, inode);
83         if (retnamlen == 0) return 0;
84         std = sizeof(struct iso_directory_record) + de->name_len[0];
85         if (std & 1) std++;
86         if ((*((unsigned char *) de) - std) != 32) return retnamlen;
87         chr = ((unsigned char *) de) + std;
88         if (strncmp(chr, "ARCHIMEDES", 10)) return retnamlen;
89         if ((*retname == '_') && ((chr[19] & 1) == 1)) *retname = '!';
90         if (((de->flags[0] & 2) == 0) && (chr[13] == 0xff)
91                 && ((chr[12] & 0xf0) == 0xf0))
92         {
93                 retname[retnamlen] = ',';
94                 sprintf(retname+retnamlen+1, "%3.3x",
95                         ((chr[12] & 0xf) << 8) | chr[11]);
96                 retnamlen += 4;
97         }
98         return retnamlen;
99 }
100
101 /*
102  * This should _really_ be cleaned up some day..
103  */
104 static int do_isofs_readdir(struct inode *inode, struct file *filp,
105                 void *dirent, filldir_t filldir,
106                 char * tmpname, struct iso_directory_record * tmpde)
107 {
108         unsigned long bufsize = ISOFS_BUFFER_SIZE(inode);
109         unsigned char bufbits = ISOFS_BUFFER_BITS(inode);
110         unsigned long block, offset, block_saved, offset_saved;
111         unsigned long inode_number = 0; /* Quiet GCC */
112         struct buffer_head *bh = NULL;
113         int len;
114         int map;
115         int first_de = 1;
116         char *p = NULL;         /* Quiet GCC */
117         struct iso_directory_record *de;
118         struct isofs_sb_info *sbi = ISOFS_SB(inode->i_sb);
119
120         offset = filp->f_pos & (bufsize - 1);
121         block = filp->f_pos >> bufbits;
122
123         while (filp->f_pos < inode->i_size) {
124                 int de_len;
125
126                 if (!bh) {
127                         bh = isofs_bread(inode, block);
128                         if (!bh)
129                                 return 0;
130                 }
131
132                 de = (struct iso_directory_record *) (bh->b_data + offset);
133
134                 de_len = *(unsigned char *) de;
135
136                 /* If the length byte is zero, we should move on to the next
137                    CDROM sector.  If we are at the end of the directory, we
138                    kick out of the while loop. */
139
140                 if (de_len == 0) {
141                         brelse(bh);
142                         bh = NULL;
143                         filp->f_pos = (filp->f_pos + ISOFS_BLOCK_SIZE) & ~(ISOFS_BLOCK_SIZE - 1);
144                         block = filp->f_pos >> bufbits;
145                         offset = 0;
146                         continue;
147                 }
148
149                 block_saved = block;
150                 offset_saved = offset;
151                 offset += de_len;
152
153                 /* Make sure we have a full directory entry */
154                 if (offset >= bufsize) {
155                         int slop = bufsize - offset + de_len;
156                         memcpy(tmpde, de, slop);
157                         offset &= bufsize - 1;
158                         block++;
159                         brelse(bh);
160                         bh = NULL;
161                         if (offset) {
162                                 bh = isofs_bread(inode, block);
163                                 if (!bh)
164                                         return 0;
165                                 memcpy((void *) tmpde + slop, bh->b_data, offset);
166                         }
167                         de = tmpde;
168                 }
169
170                 if (first_de) {
171                         isofs_normalize_block_and_offset(de,
172                                                          &block_saved,
173                                                          &offset_saved);
174                         inode_number = isofs_get_ino(block_saved,
175                                                      offset_saved,
176                                                      bufbits);
177                 }
178
179                 if (de->flags[-sbi->s_high_sierra] & 0x80) {
180                         first_de = 0;
181                         filp->f_pos += de_len;
182                         continue;
183                 }
184                 first_de = 1;
185
186                 /* Handle the case of the '.' directory */
187                 if (de->name_len[0] == 1 && de->name[0] == 0) {
188                         if (filldir(dirent, ".", 1, filp->f_pos, inode->i_ino, DT_DIR) < 0)
189                                 break;
190                         filp->f_pos += de_len;
191                         continue;
192                 }
193
194                 len = 0;
195
196                 /* Handle the case of the '..' directory */
197                 if (de->name_len[0] == 1 && de->name[0] == 1) {
198                         inode_number = parent_ino(filp->f_dentry);
199                         if (filldir(dirent, "..", 2, filp->f_pos, inode_number, DT_DIR) < 0)
200                                 break;
201                         filp->f_pos += de_len;
202                         continue;
203                 }
204
205                 /* Handle everything else.  Do name translation if there
206                    is no Rock Ridge NM field. */
207                 if (sbi->s_unhide == 'n') {
208                         /* Do not report hidden or associated files */
209                         if (de->flags[-sbi->s_high_sierra] & 5) {
210                                 filp->f_pos += de_len;
211                                 continue;
212                         }
213                 }
214
215                 map = 1;
216                 if (sbi->s_rock) {
217                         len = get_rock_ridge_filename(de, tmpname, inode);
218                         if (len != 0) {         /* may be -1 */
219                                 p = tmpname;
220                                 map = 0;
221                         }
222                 }
223                 if (map) {
224 #ifdef CONFIG_JOLIET
225                         if (sbi->s_joliet_level) {
226                                 len = get_joliet_filename(de, tmpname, inode);
227                                 p = tmpname;
228                         } else
229 #endif
230                         if (sbi->s_mapping == 'a') {
231                                 len = get_acorn_filename(de, tmpname, inode);
232                                 p = tmpname;
233                         } else
234                         if (sbi->s_mapping == 'n') {
235                                 len = isofs_name_translate(de, tmpname, inode);
236                                 p = tmpname;
237                         } else {
238                                 p = de->name;
239                                 len = de->name_len[0];
240                         }
241                 }
242                 if (len > 0) {
243                         if (filldir(dirent, p, len, filp->f_pos, inode_number, DT_UNKNOWN) < 0)
244                                 break;
245                 }
246                 filp->f_pos += de_len;
247
248                 continue;
249         }
250         if (bh) brelse(bh);
251         return 0;
252 }
253
254 /*
255  * Handle allocation of temporary space for name translation and
256  * handling split directory entries.. The real work is done by
257  * "do_isofs_readdir()".
258  */
259 static int isofs_readdir(struct file *filp,
260                 void *dirent, filldir_t filldir)
261 {
262         int result;
263         char * tmpname;
264         struct iso_directory_record * tmpde;
265         struct inode *inode = filp->f_dentry->d_inode;
266
267
268         tmpname = (char *)__get_free_page(GFP_KERNEL);
269         if (tmpname == NULL)
270                 return -ENOMEM;
271
272         lock_kernel();
273         tmpde = (struct iso_directory_record *) (tmpname+1024);
274
275         result = do_isofs_readdir(inode, filp, dirent, filldir, tmpname, tmpde);
276
277         free_page((unsigned long) tmpname);
278         unlock_kernel();
279         return result;
280 }