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