blob: 5440ea292c6964678ce6038db108633d69363962 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
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 */
Linus Torvalds1da177e2005-04-16 15:20:36 -070013#include <linux/config.h>
14#include <linux/smp_lock.h>
Al Viro94f2f712005-04-25 18:32:12 -070015#include "isofs.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070016
17static int isofs_readdir(struct file *, void *, filldir_t);
18
Arjan van de Ven4b6f5d22006-03-28 01:56:42 -080019const struct file_operations isofs_dir_operations =
Linus Torvalds1da177e2005-04-16 15:20:36 -070020{
21 .read = generic_read_dir,
22 .readdir = isofs_readdir,
23};
24
25/*
26 * directories can handle most operations...
27 */
28struct inode_operations isofs_dir_inode_operations =
29{
30 .lookup = isofs_lookup,
31};
32
33int 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 */
66int 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 */
93static 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. */
Jeremy White9769f4e2005-06-21 17:16:53 -0700196
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;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700207 }
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 */
253static 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}