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