Linux-2.6.12-rc2
[GitHub/mt8127/android_kernel_alcatel_ttab.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 }