ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[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                 if (c == ';')
68                         c = '.';
69
70                 new[i] = c;
71         }
72         return i;
73 }
74
75 /* Acorn extensions written by Matthew Wilcox <willy@bofh.ai> 1998 */
76 int get_acorn_filename(struct iso_directory_record * de,
77                             char * retname, struct inode * inode)
78 {
79         int std;
80         unsigned char * chr;
81         int retnamlen = isofs_name_translate(de, retname, inode);
82         if (retnamlen == 0) return 0;
83         std = sizeof(struct iso_directory_record) + de->name_len[0];
84         if (std & 1) std++;
85         if ((*((unsigned char *) de) - std) != 32) return retnamlen;
86         chr = ((unsigned char *) de) + std;
87         if (strncmp(chr, "ARCHIMEDES", 10)) return retnamlen;
88         if ((*retname == '_') && ((chr[19] & 1) == 1)) *retname = '!';
89         if (((de->flags[0] & 2) == 0) && (chr[13] == 0xff)
90                 && ((chr[12] & 0xf0) == 0xf0))
91         {
92                 retname[retnamlen] = ',';
93                 sprintf(retname+retnamlen+1, "%3.3x",
94                         ((chr[12] & 0xf) << 8) | chr[11]);
95                 retnamlen += 4;
96         }
97         return retnamlen;
98 }
99
100 /*
101  * This should _really_ be cleaned up some day..
102  */
103 static int do_isofs_readdir(struct inode *inode, struct file *filp,
104                 void *dirent, filldir_t filldir,
105                 char * tmpname, struct iso_directory_record * tmpde)
106 {
107         unsigned long bufsize = ISOFS_BUFFER_SIZE(inode);
108         unsigned char bufbits = ISOFS_BUFFER_BITS(inode);
109         unsigned int block, offset;
110         int inode_number = 0;   /* Quiet GCC */
111         struct buffer_head *bh = NULL;
112         int len;
113         int map;
114         int first_de = 1;
115         char *p = NULL;         /* Quiet GCC */
116         struct iso_directory_record *de;
117         struct isofs_sb_info *sbi = ISOFS_SB(inode->i_sb);
118
119         offset = filp->f_pos & (bufsize - 1);
120         block = filp->f_pos >> bufbits;
121
122         while (filp->f_pos < inode->i_size) {
123                 int de_len;
124
125                 if (!bh) {
126                         bh = isofs_bread(inode, block);
127                         if (!bh)
128                                 return 0;
129                 }
130
131                 de = (struct iso_directory_record *) (bh->b_data + offset);
132                 if (first_de)
133                         inode_number = (bh->b_blocknr << bufbits) + offset;
134
135                 de_len = *(unsigned char *) de;
136
137                 /* If the length byte is zero, we should move on to the next
138                    CDROM sector.  If we are at the end of the directory, we
139                    kick out of the while loop. */
140
141                 if (de_len == 0) {
142                         brelse(bh);
143                         bh = NULL;
144                         filp->f_pos = (filp->f_pos + ISOFS_BLOCK_SIZE) & ~(ISOFS_BLOCK_SIZE - 1);
145                         block = filp->f_pos >> bufbits;
146                         offset = 0;
147                         continue;
148                 }
149
150                 offset += de_len;
151
152                 /* Make sure we have a full directory entry */
153                 if (offset >= bufsize) {
154                         int slop = bufsize - offset + de_len;
155                         memcpy(tmpde, de, slop);
156                         offset &= bufsize - 1;
157                         block++;
158                         brelse(bh);
159                         bh = NULL;
160                         if (offset) {
161                                 bh = isofs_bread(inode, block);
162                                 if (!bh)
163                                         return 0;
164                                 memcpy((void *) tmpde + slop, bh->b_data, offset);
165                         }
166                         de = tmpde;
167                 }
168
169                 if (de->flags[-sbi->s_high_sierra] & 0x80) {
170                         first_de = 0;
171                         filp->f_pos += de_len;
172                         continue;
173                 }
174                 first_de = 1;
175
176                 /* Handle the case of the '.' directory */
177                 if (de->name_len[0] == 1 && de->name[0] == 0) {
178                         if (filldir(dirent, ".", 1, filp->f_pos, inode->i_ino, DT_DIR) < 0)
179                                 break;
180                         filp->f_pos += de_len;
181                         continue;
182                 }
183
184                 len = 0;
185
186                 /* Handle the case of the '..' directory */
187                 if (de->name_len[0] == 1 && de->name[0] == 1) {
188                         inode_number = parent_ino(filp->f_dentry);
189                         if (filldir(dirent, "..", 2, filp->f_pos, inode_number, DT_DIR) < 0)
190                                 break;
191                         filp->f_pos += de_len;
192                         continue;
193                 }
194
195                 /* Handle everything else.  Do name translation if there
196                    is no Rock Ridge NM field. */
197                 if (sbi->s_unhide == 'n') {
198                         /* Do not report hidden or associated files */
199                         if (de->flags[-sbi->s_high_sierra] & 5) {
200                                 filp->f_pos += de_len;
201                                 continue;
202                         }
203                 }
204
205                 map = 1;
206                 if (sbi->s_rock) {
207                         len = get_rock_ridge_filename(de, tmpname, inode);
208                         if (len != 0) {         /* may be -1 */
209                                 p = tmpname;
210                                 map = 0;
211                         }
212                 }
213                 if (map) {
214 #ifdef CONFIG_JOLIET
215                         if (sbi->s_joliet_level) {
216                                 len = get_joliet_filename(de, tmpname, inode);
217                                 p = tmpname;
218                         } else
219 #endif
220                         if (sbi->s_mapping == 'a') {
221                                 len = get_acorn_filename(de, tmpname, inode);
222                                 p = tmpname;
223                         } else
224                         if (sbi->s_mapping == 'n') {
225                                 len = isofs_name_translate(de, tmpname, inode);
226                                 p = tmpname;
227                         } else {
228                                 p = de->name;
229                                 len = de->name_len[0];
230                         }
231                 }
232                 if (len > 0) {
233                         if (filldir(dirent, p, len, filp->f_pos, inode_number, DT_UNKNOWN) < 0)
234                                 break;
235                 }
236                 filp->f_pos += de_len;
237
238                 continue;
239         }
240         if (bh) brelse(bh);
241         return 0;
242 }
243
244 /*
245  * Handle allocation of temporary space for name translation and
246  * handling split directory entries.. The real work is done by
247  * "do_isofs_readdir()".
248  */
249 static int isofs_readdir(struct file *filp,
250                 void *dirent, filldir_t filldir)
251 {
252         int result;
253         char * tmpname;
254         struct iso_directory_record * tmpde;
255         struct inode *inode = filp->f_dentry->d_inode;
256
257
258         tmpname = (char *)__get_free_page(GFP_KERNEL);
259         if (tmpname == NULL)
260                 return -ENOMEM;
261
262         lock_kernel();
263         tmpde = (struct iso_directory_record *) (tmpname+1024);
264
265         result = do_isofs_readdir(inode, filp, dirent, filldir, tmpname, tmpde);
266
267         free_page((unsigned long) tmpname);
268         unlock_kernel();
269         return result;
270 }