- Andries Brouwer: final isofs pieces.
[davej-history.git] / fs / isofs / dir.c
blob6f0957dce489322dd0996a9888a6216899ec59fa
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/malloc.h>
21 #include <linux/sched.h>
22 #include <linux/locks.h>
23 #include <linux/config.h>
25 #include <asm/uaccess.h>
27 static int isofs_readdir(struct file *, void *, filldir_t);
29 struct file_operations isofs_dir_operations =
31 read: generic_read_dir,
32 readdir: isofs_readdir,
36 * directories can handle most operations...
38 struct inode_operations isofs_dir_inode_operations =
40 lookup: isofs_lookup,
43 int isofs_name_translate(struct iso_directory_record *de, char *new, struct inode *inode)
45 char * old = de->name;
46 int len = de->name_len[0];
47 int i;
49 for (i = 0; i < len; i++) {
50 unsigned char c = old[i];
51 if (!c)
52 break;
54 if (c >= 'A' && c <= 'Z')
55 c |= 0x20; /* lower case */
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;
61 /* Drop trailing ';1' */
62 if (c == ';' && i == len - 2 && old[i + 1] == '1')
63 break;
65 /* Convert remaining ';' to '.' */
66 if (c == ';')
67 c = '.';
69 new[i] = c;
71 return i;
74 /* Acorn extensions written by Matthew Wilcox <willy@bofh.ai> 1998 */
75 int get_acorn_filename(struct iso_directory_record * de,
76 char * retname, struct inode * inode)
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))
91 retname[retnamlen] = ',';
92 sprintf(retname+retnamlen+1, "%3.3x",
93 ((chr[12] & 0xf) << 8) | chr[11]);
94 retnamlen += 4;
96 return retnamlen;
100 * This should _really_ be cleaned up some day..
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)
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;
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;
122 while (filp->f_pos < inode->i_size) {
123 int de_len;
125 if (!bh) {
126 bh = isofs_bread(inode, bufsize, block);
127 if (!bh)
128 return 0;
131 de = (struct iso_directory_record *) (bh->b_data + offset);
132 if (first_de)
133 inode_number = (bh->b_blocknr << bufbits) + offset;
135 de_len = *(unsigned char *) de;
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. */
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;
150 offset += de_len;
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, bufsize, block);
162 if (!bh)
163 return 0;
164 memcpy((void *) tmpde + slop, bh->b_data, offset);
166 de = tmpde;
169 if (de->flags[-high_sierra] & 0x80) {
170 first_de = 0;
171 filp->f_pos += de_len;
172 continue;
174 first_de = 1;
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;
184 len = 0;
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;
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;
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;
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];
232 if (len > 0) {
233 if (filldir(dirent, p, len, filp->f_pos, inode_number, DT_UNKNOWN) < 0)
234 break;
236 filp->f_pos += de_len;
238 continue;
240 if (bh) brelse(bh);
241 return 0;
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()".
249 static int isofs_readdir(struct file *filp,
250 void *dirent, filldir_t filldir)
252 int result;
253 char * tmpname;
254 struct iso_directory_record * tmpde;
255 struct inode *inode = filp->f_dentry->d_inode;
257 tmpname = (char *) __get_free_page(GFP_KERNEL);
258 if (!tmpname)
259 return -ENOMEM;
260 tmpde = (struct iso_directory_record *) (tmpname+1024);
262 result = do_isofs_readdir(inode, filp, dirent, filldir, tmpname, tmpde);
264 free_page((unsigned long) tmpname);
265 return result;