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/sched.h>
22 #include <linux/locks.h>
23 #include <linux/config.h>
24
25 #include <asm/uaccess.h>
26
27 static int isofs_readdir(struct file *, void *, filldir_t);
28
29 struct file_operations isofs_dir_operations =
30 {
31 read: generic_read_dir,
32 readdir: isofs_readdir,
33 };
34
35 /*
36 * directories can handle most operations...
37 */
38 struct inode_operations isofs_dir_inode_operations =
39 {
40 lookup: isofs_lookup,
41 };
42
isofs_name_translate(struct iso_directory_record * de,char * new,struct inode * inode)43 int isofs_name_translate(struct iso_directory_record *de, char *new, struct inode *inode)
44 {
45 char * old = de->name;
46 int len = de->name_len[0];
47 int i;
48
49 for (i = 0; i < len; i++) {
50 unsigned char c = old[i];
51 if (!c)
52 break;
53
54 if (c >= 'A' && c <= 'Z')
55 c |= 0x20; /* lower case */
56
57 /* Drop trailing '.;1' (ISO 9660:1988 7.5.1 requires period) */
58 if (c == '.' && i == len - 3 && old[i + 1] == ';' && old[i + 2] == '1')
59 break;
60
61 /* Drop trailing ';1' */
62 if (c == ';' && i == len - 2 && old[i + 1] == '1')
63 break;
64
65 /* Convert remaining ';' to '.' */
66 if (c == ';')
67 c = '.';
68
69 new[i] = c;
70 }
71 return i;
72 }
73
74 /* Acorn extensions written by Matthew Wilcox <willy@bofh.ai> 1998 */
get_acorn_filename(struct iso_directory_record * de,char * retname,struct inode * inode)75 int get_acorn_filename(struct iso_directory_record * de,
76 char * retname, struct inode * inode)
77 {
78 int std;
79 unsigned char * chr;
80 int retnamlen = isofs_name_translate(de, retname, inode);
81 if (retnamlen == 0) return 0;
82 std = sizeof(struct iso_directory_record) + de->name_len[0];
83 if (std & 1) std++;
84 if ((*((unsigned char *) de) - std) != 32) return retnamlen;
85 chr = ((unsigned char *) de) + std;
86 if (strncmp(chr, "ARCHIMEDES", 10)) return retnamlen;
87 if ((*retname == '_') && ((chr[19] & 1) == 1)) *retname = '!';
88 if (((de->flags[0] & 2) == 0) && (chr[13] == 0xff)
89 && ((chr[12] & 0xf0) == 0xf0))
90 {
91 retname[retnamlen] = ',';
92 sprintf(retname+retnamlen+1, "%3.3x",
93 ((chr[12] & 0xf) << 8) | chr[11]);
94 retnamlen += 4;
95 }
96 return retnamlen;
97 }
98
99 /*
100 * This should _really_ be cleaned up some day..
101 */
do_isofs_readdir(struct inode * inode,struct file * filp,void * dirent,filldir_t filldir,char * tmpname,struct iso_directory_record * tmpde)102 static int do_isofs_readdir(struct inode *inode, struct file *filp,
103 void *dirent, filldir_t filldir,
104 char * tmpname, struct iso_directory_record * tmpde)
105 {
106 unsigned long bufsize = ISOFS_BUFFER_SIZE(inode);
107 unsigned char bufbits = ISOFS_BUFFER_BITS(inode);
108 unsigned int block, offset;
109 int inode_number = 0; /* Quiet GCC */
110 struct buffer_head *bh = NULL;
111 int len;
112 int map;
113 int high_sierra;
114 int first_de = 1;
115 char *p = NULL; /* Quiet GCC */
116 struct iso_directory_record *de;
117
118 offset = filp->f_pos & (bufsize - 1);
119 block = filp->f_pos >> bufbits;
120 high_sierra = inode->i_sb->u.isofs_sb.s_high_sierra;
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[-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 = filp->f_dentry->d_parent->d_inode->i_ino;
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 (inode->i_sb->u.isofs_sb.s_unhide == 'n') {
198 /* Do not report hidden or associated files */
199 if (de->flags[-high_sierra] & 5) {
200 filp->f_pos += de_len;
201 continue;
202 }
203 }
204
205 map = 1;
206 if (inode->i_sb->u.isofs_sb.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 (inode->i_sb->u.isofs_sb.s_joliet_level) {
216 len = get_joliet_filename(de, tmpname, inode);
217 p = tmpname;
218 } else
219 #endif
220 if (inode->i_sb->u.isofs_sb.s_mapping == 'a') {
221 len = get_acorn_filename(de, tmpname, inode);
222 p = tmpname;
223 } else
224 if (inode->i_sb->u.isofs_sb.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 */
isofs_readdir(struct file * filp,void * dirent,filldir_t filldir)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 tmpname = (char *) __get_free_page(GFP_KERNEL);
258 if (!tmpname)
259 return -ENOMEM;
260 tmpde = (struct iso_directory_record *) (tmpname+1024);
261
262 result = do_isofs_readdir(inode, filp, dirent, filldir, tmpname, tmpde);
263
264 free_page((unsigned long) tmpname);
265 return result;
266 }
267