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/time.h> 22 #include <linux/config.h> 23 #include <linux/smp_lock.h> 24 #include <linux/buffer_head.h> 25 26 #include <asm/uaccess.h> 27 28 static int isofs_readdir(struct file *, void *, filldir_t); 29 30 struct file_operations isofs_dir_operations = 31 { 32 .read = generic_read_dir, 33 .readdir = isofs_readdir, 34 }; 35 36 /* 37 * directories can handle most operations... 38 */ 39 struct inode_operations isofs_dir_inode_operations = 40 { 41 .lookup = isofs_lookup, 42 }; 43 44 int isofs_name_translate(struct iso_directory_record *de, char *new, struct inode *inode) 45 { 46 char * old = de->name; 47 int len = de->name_len[0]; 48 int i; 49 50 for (i = 0; i < len; i++) { 51 unsigned char c = old[i]; 52 if (!c) 53 break; 54 55 if (c >= 'A' && c <= 'Z') 56 c |= 0x20; /* lower case */ 57 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; 61 62 /* Drop trailing ';1' */ 63 if (c == ';' && i == len - 2 && old[i + 1] == '1') 64 break; 65 66 /* Convert remaining ';' to '.' */ 67 /* Also '/' to '.' (broken Acorn-generated ISO9660 images) */ 68 if (c == ';' || c == '/') 69 c = '.'; 70 71 new[i] = c; 72 } 73 return i; 74 } 75 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) 79 { 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)) 92 { 93 retname[retnamlen] = ','; 94 sprintf(retname+retnamlen+1, "%3.3x", 95 ((chr[12] & 0xf) << 8) | chr[11]); 96 retnamlen += 4; 97 } 98 return retnamlen; 99 } 100 101 /* 102 * This should _really_ be cleaned up some day.. 103 */ 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) 107 { 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); 119 120 offset = filp->f_pos & (bufsize - 1); 121 block = filp->f_pos >> bufbits; 122 123 while (filp->f_pos < inode->i_size) { 124 int de_len; 125 126 if (!bh) { 127 bh = isofs_bread(inode, block); 128 if (!bh) 129 return 0; 130 } 131 132 de = (struct iso_directory_record *) (bh->b_data + offset); 133 134 de_len = *(unsigned char *) de; 135 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. */ 139 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; 147 } 148 149 block_saved = block; 150 offset_saved = offset; 151 offset += de_len; 152 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); 166 } 167 de = tmpde; 168 } 169 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); 177 } 178 179 if (de->flags[-sbi->s_high_sierra] & 0x80) { 180 first_de = 0; 181 filp->f_pos += de_len; 182 continue; 183 } 184 first_de = 1; 185 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; 192 } 193 194 len = 0; 195 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; 203 } 204 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; 212 } 213 } 214 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; 221 } 222 } 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]; 240 } 241 } 242 if (len > 0) { 243 if (filldir(dirent, p, len, filp->f_pos, inode_number, DT_UNKNOWN) < 0) 244 break; 245 } 246 filp->f_pos += de_len; 247 248 continue; 249 } 250 if (bh) brelse(bh); 251 return 0; 252 } 253 254 /* 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()". 258 */ 259 static int isofs_readdir(struct file *filp, 260 void *dirent, filldir_t filldir) 261 { 262 int result; 263 char * tmpname; 264 struct iso_directory_record * tmpde; 265 struct inode *inode = filp->f_dentry->d_inode; 266 267 268 tmpname = (char *)__get_free_page(GFP_KERNEL); 269 if (tmpname == NULL) 270 return -ENOMEM; 271 272 lock_kernel(); 273 tmpde = (struct iso_directory_record *) (tmpname+1024); 274 275 result = do_isofs_readdir(inode, filp, dirent, filldir, tmpname, tmpde); 276 277 free_page((unsigned long) tmpname); 278 unlock_kernel(); 279 return result; 280 } 281