Linux-2.6.12-rc2
[linux-2.6/next.git] / fs / isofs / dir.c
blob14d86de6637d25cb1404a8d83774e4a1bd420ef9
1 /*
2 * linux/fs/isofs/dir.c
4 * (C) 1992, 1993, 1994 Eric Youngdale Modified for ISO 9660 filesystem.
6 * (C) 1991 Linus Torvalds - minix filesystem
8 * Steve Beynon : Missing last directory entries fixed
9 * (stephen@askone.demon.co.uk) : 21st June 1996
11 * isofs directory handling functions
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>
26 #include <asm/uaccess.h>
28 static int isofs_readdir(struct file *, void *, filldir_t);
30 struct file_operations isofs_dir_operations =
32 .read = generic_read_dir,
33 .readdir = isofs_readdir,
37 * directories can handle most operations...
39 struct inode_operations isofs_dir_inode_operations =
41 .lookup = isofs_lookup,
44 int isofs_name_translate(struct iso_directory_record *de, char *new, struct inode *inode)
46 char * old = de->name;
47 int len = de->name_len[0];
48 int i;
50 for (i = 0; i < len; i++) {
51 unsigned char c = old[i];
52 if (!c)
53 break;
55 if (c >= 'A' && c <= 'Z')
56 c |= 0x20; /* lower case */
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;
62 /* Drop trailing ';1' */
63 if (c == ';' && i == len - 2 && old[i + 1] == '1')
64 break;
66 /* Convert remaining ';' to '.' */
67 /* Also '/' to '.' (broken Acorn-generated ISO9660 images) */
68 if (c == ';' || c == '/')
69 c = '.';
71 new[i] = c;
73 return i;
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)
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))
93 retname[retnamlen] = ',';
94 sprintf(retname+retnamlen+1, "%3.3x",
95 ((chr[12] & 0xf) << 8) | chr[11]);
96 retnamlen += 4;
98 return retnamlen;
102 * This should _really_ be cleaned up some day..
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)
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);
120 offset = filp->f_pos & (bufsize - 1);
121 block = filp->f_pos >> bufbits;
123 while (filp->f_pos < inode->i_size) {
124 int de_len;
126 if (!bh) {
127 bh = isofs_bread(inode, block);
128 if (!bh)
129 return 0;
132 de = (struct iso_directory_record *) (bh->b_data + offset);
134 de_len = *(unsigned char *) de;
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. */
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;
149 block_saved = block;
150 offset_saved = offset;
151 offset += de_len;
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);
167 de = tmpde;
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);
179 if (de->flags[-sbi->s_high_sierra] & 0x80) {
180 first_de = 0;
181 filp->f_pos += de_len;
182 continue;
184 first_de = 1;
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;
194 len = 0;
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;
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;
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;
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];
242 if (len > 0) {
243 if (filldir(dirent, p, len, filp->f_pos, inode_number, DT_UNKNOWN) < 0)
244 break;
246 filp->f_pos += de_len;
248 continue;
250 if (bh) brelse(bh);
251 return 0;
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()".
259 static int isofs_readdir(struct file *filp,
260 void *dirent, filldir_t filldir)
262 int result;
263 char * tmpname;
264 struct iso_directory_record * tmpde;
265 struct inode *inode = filp->f_dentry->d_inode;
268 tmpname = (char *)__get_free_page(GFP_KERNEL);
269 if (tmpname == NULL)
270 return -ENOMEM;
272 lock_kernel();
273 tmpde = (struct iso_directory_record *) (tmpname+1024);
275 result = do_isofs_readdir(inode, filp, dirent, filldir, tmpname, tmpde);
277 free_page((unsigned long) tmpname);
278 unlock_kernel();
279 return result;