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>
15 #include <linux/iso_fs.h>
16 #include <linux/kernel.h>
17 #include <linux/stat.h>
18 #include <linux/string.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];
50 for (i = 0; i < len; i++) {
51 unsigned char c = old[i];
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')
62 /* Drop trailing ';1' */
63 if (c == ';' && i == len - 2 && old[i + 1] == '1')
66 /* Convert remaining ';' to '.' */
75 /* Acorn extensions written by Matthew Wilcox <willy@bofh.ai> 1998 */
76 int get_acorn_filename(struct iso_directory_record * de,
77 char * retname, struct inode * inode)
81 int retnamlen = isofs_name_translate(de, retname, inode);
82 if (retnamlen == 0) return 0;
83 std = sizeof(struct iso_directory_record) + de->name_len[0];
85 if ((*((unsigned char *) de) - std) != 32) return retnamlen;
86 chr = ((unsigned char *) de) + std;
87 if (strncmp(chr, "ARCHIMEDES", 10)) return retnamlen;
88 if ((*retname == '_') && ((chr[19] & 1) == 1)) *retname = '!';
89 if (((de->flags[0] & 2) == 0) && (chr[13] == 0xff)
90 && ((chr[12] & 0xf0) == 0xf0))
92 retname[retnamlen] = ',';
93 sprintf(retname+retnamlen+1, "%3.3x",
94 ((chr[12] & 0xf) << 8) | chr[11]);
101 * This should _really_ be cleaned up some day..
103 static int do_isofs_readdir(struct inode *inode, struct file *filp,
104 void *dirent, filldir_t filldir,
105 char * tmpname, struct iso_directory_record * tmpde)
107 unsigned long bufsize = ISOFS_BUFFER_SIZE(inode);
108 unsigned char bufbits = ISOFS_BUFFER_BITS(inode);
109 unsigned int block, offset;
110 int inode_number = 0; /* Quiet GCC */
111 struct buffer_head *bh = NULL;
115 char *p = NULL; /* Quiet GCC */
116 struct iso_directory_record *de;
117 struct isofs_sb_info *sbi = ISOFS_SB(inode->i_sb);
119 offset = filp->f_pos & (bufsize - 1);
120 block = filp->f_pos >> bufbits;
122 while (filp->f_pos < inode->i_size) {
126 bh = isofs_bread(inode, block);
131 de = (struct iso_directory_record *) (bh->b_data + offset);
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. */
144 filp->f_pos = (filp->f_pos + ISOFS_BLOCK_SIZE) & ~(ISOFS_BLOCK_SIZE - 1);
145 block = filp->f_pos >> bufbits;
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;
161 bh = isofs_bread(inode, block);
164 memcpy((void *) tmpde + slop, bh->b_data, offset);
169 if (de->flags[-sbi->s_high_sierra] & 0x80) {
171 filp->f_pos += de_len;
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)
180 filp->f_pos += de_len;
186 /* Handle the case of the '..' directory */
187 if (de->name_len[0] == 1 && de->name[0] == 1) {
188 inode_number = parent_ino(filp->f_dentry);
189 if (filldir(dirent, "..", 2, filp->f_pos, inode_number, DT_DIR) < 0)
191 filp->f_pos += de_len;
195 /* Handle everything else. Do name translation if there
196 is no Rock Ridge NM field. */
197 if (sbi->s_unhide == 'n') {
198 /* Do not report hidden or associated files */
199 if (de->flags[-sbi->s_high_sierra] & 5) {
200 filp->f_pos += de_len;
207 len = get_rock_ridge_filename(de, tmpname, inode);
208 if (len != 0) { /* may be -1 */
215 if (sbi->s_joliet_level) {
216 len = get_joliet_filename(de, tmpname, inode);
220 if (sbi->s_mapping == 'a') {
221 len = get_acorn_filename(de, tmpname, inode);
224 if (sbi->s_mapping == 'n') {
225 len = isofs_name_translate(de, tmpname, inode);
229 len = de->name_len[0];
233 if (filldir(dirent, p, len, filp->f_pos, inode_number, DT_UNKNOWN) < 0)
236 filp->f_pos += de_len;
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)
254 struct iso_directory_record * tmpde;
255 struct inode *inode = filp->f_dentry->d_inode;
258 tmpname = (char *)__get_free_page(GFP_KERNEL);
263 tmpde = (struct iso_directory_record *) (tmpname+1024);
265 result = do_isofs_readdir(inode, filp, dirent, filldir, tmpname, tmpde);
267 free_page((unsigned long) tmpname);