Merge with /pub/scm/linux/kernel/git/torvalds/linux-2.6.git
[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/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
197                 /*
198                  * Do not report hidden files if so instructed, or associated
199                  * files unless instructed to do so
200                  */
201                 if ((sbi->s_hide == 'y' &&
202                                 (de->flags[-sbi->s_high_sierra] & 1)) ||
203                       (sbi->s_showassoc =='n' &&
204                                 (de->flags[-sbi->s_high_sierra] & 4))) {
205                         filp->f_pos += de_len;
206                         continue;
207                 }
208
209                 map = 1;
210                 if (sbi->s_rock) {
211                         len = get_rock_ridge_filename(de, tmpname, inode);
212                         if (len != 0) {         /* may be -1 */
213                                 p = tmpname;
214                                 map = 0;
215                         }
216                 }
217                 if (map) {
218 #ifdef CONFIG_JOLIET
219                         if (sbi->s_joliet_level) {
220                                 len = get_joliet_filename(de, tmpname, inode);
221                                 p = tmpname;
222                         } else
223 #endif
224                         if (sbi->s_mapping == 'a') {
225                                 len = get_acorn_filename(de, tmpname, inode);
226                                 p = tmpname;
227                         } else
228                         if (sbi->s_mapping == 'n') {
229                                 len = isofs_name_translate(de, tmpname, inode);
230                                 p = tmpname;
231                         } else {
232                                 p = de->name;
233                                 len = de->name_len[0];
234                         }
235                 }
236                 if (len > 0) {
237                         if (filldir(dirent, p, len, filp->f_pos, inode_number, DT_UNKNOWN) < 0)
238                                 break;
239                 }
240                 filp->f_pos += de_len;
241
242                 continue;
243         }
244         if (bh) brelse(bh);
245         return 0;
246 }
247
248 /*
249  * Handle allocation of temporary space for name translation and
250  * handling split directory entries.. The real work is done by
251  * "do_isofs_readdir()".
252  */
253 static int isofs_readdir(struct file *filp,
254                 void *dirent, filldir_t filldir)
255 {
256         int result;
257         char * tmpname;
258         struct iso_directory_record * tmpde;
259         struct inode *inode = filp->f_dentry->d_inode;
260
261
262         tmpname = (char *)__get_free_page(GFP_KERNEL);
263         if (tmpname == NULL)
264                 return -ENOMEM;
265
266         lock_kernel();
267         tmpde = (struct iso_directory_record *) (tmpname+1024);
268
269         result = do_isofs_readdir(inode, filp, dirent, filldir, tmpname, tmpde);
270
271         free_page((unsigned long) tmpname);
272         unlock_kernel();
273         return result;
274 }