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 27static int isofs_readdir(struct file *, void *, filldir_t); 28 29struct 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 */ 38struct inode_operations isofs_dir_inode_operations = 39{ 40 lookup: isofs_lookup, 41}; 42 43int 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 */ 75int 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 */ 102static 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 */ 249static 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