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/smp_lock.h> 14 #include "isofs.h" 15 16 static int isofs_readdir(struct file *, void *, filldir_t); 17 18 const struct file_operations isofs_dir_operations = 19 { 20 .read = generic_read_dir, 21 .readdir = isofs_readdir, 22 }; 23 24 /* 25 * directories can handle most operations... 26 */ 27 const struct inode_operations isofs_dir_inode_operations = 28 { 29 .lookup = isofs_lookup, 30 }; 31 32 int isofs_name_translate(struct iso_directory_record *de, char *new, struct inode *inode) 33 { 34 char * old = de->name; 35 int len = de->name_len[0]; 36 int i; 37 38 for (i = 0; i < len; i++) { 39 unsigned char c = old[i]; 40 if (!c) 41 break; 42 43 if (c >= 'A' && c <= 'Z') 44 c |= 0x20; /* lower case */ 45 46 /* Drop trailing '.;1' (ISO 9660:1988 7.5.1 requires period) */ 47 if (c == '.' && i == len - 3 && old[i + 1] == ';' && old[i + 2] == '1') 48 break; 49 50 /* Drop trailing ';1' */ 51 if (c == ';' && i == len - 2 && old[i + 1] == '1') 52 break; 53 54 /* Convert remaining ';' to '.' */ 55 /* Also '/' to '.' (broken Acorn-generated ISO9660 images) */ 56 if (c == ';' || c == '/') 57 c = '.'; 58 59 new[i] = c; 60 } 61 return i; 62 } 63 64 /* Acorn extensions written by Matthew Wilcox <willy@bofh.ai> 1998 */ 65 int get_acorn_filename(struct iso_directory_record * de, 66 char * retname, struct inode * inode) 67 { 68 int std; 69 unsigned char * chr; 70 int retnamlen = isofs_name_translate(de, retname, inode); 71 if (retnamlen == 0) return 0; 72 std = sizeof(struct iso_directory_record) + de->name_len[0]; 73 if (std & 1) std++; 74 if ((*((unsigned char *) de) - std) != 32) return retnamlen; 75 chr = ((unsigned char *) de) + std; 76 if (strncmp(chr, "ARCHIMEDES", 10)) return retnamlen; 77 if ((*retname == '_') && ((chr[19] & 1) == 1)) *retname = '!'; 78 if (((de->flags[0] & 2) == 0) && (chr[13] == 0xff) 79 && ((chr[12] & 0xf0) == 0xf0)) 80 { 81 retname[retnamlen] = ','; 82 sprintf(retname+retnamlen+1, "%3.3x", 83 ((chr[12] & 0xf) << 8) | chr[11]); 84 retnamlen += 4; 85 } 86 return retnamlen; 87 } 88 89 /* 90 * This should _really_ be cleaned up some day.. 91 */ 92 static int do_isofs_readdir(struct inode *inode, struct file *filp, 93 void *dirent, filldir_t filldir, 94 char * tmpname, struct iso_directory_record * tmpde) 95 { 96 unsigned long bufsize = ISOFS_BUFFER_SIZE(inode); 97 unsigned char bufbits = ISOFS_BUFFER_BITS(inode); 98 unsigned long block, offset, block_saved, offset_saved; 99 unsigned long inode_number = 0; /* Quiet GCC */ 100 struct buffer_head *bh = NULL; 101 int len; 102 int map; 103 int first_de = 1; 104 char *p = NULL; /* Quiet GCC */ 105 struct iso_directory_record *de; 106 struct isofs_sb_info *sbi = ISOFS_SB(inode->i_sb); 107 108 offset = filp->f_pos & (bufsize - 1); 109 block = filp->f_pos >> bufbits; 110 111 while (filp->f_pos < inode->i_size) { 112 int de_len; 113 114 if (!bh) { 115 bh = isofs_bread(inode, block); 116 if (!bh) 117 return 0; 118 } 119 120 de = (struct iso_directory_record *) (bh->b_data + offset); 121 122 de_len = *(unsigned char *) de; 123 124 /* If the length byte is zero, we should move on to the next 125 CDROM sector. If we are at the end of the directory, we 126 kick out of the while loop. */ 127 128 if (de_len == 0) { 129 brelse(bh); 130 bh = NULL; 131 filp->f_pos = (filp->f_pos + ISOFS_BLOCK_SIZE) & ~(ISOFS_BLOCK_SIZE - 1); 132 block = filp->f_pos >> bufbits; 133 offset = 0; 134 continue; 135 } 136 137 block_saved = block; 138 offset_saved = offset; 139 offset += de_len; 140 141 /* Make sure we have a full directory entry */ 142 if (offset >= bufsize) { 143 int slop = bufsize - offset + de_len; 144 memcpy(tmpde, de, slop); 145 offset &= bufsize - 1; 146 block++; 147 brelse(bh); 148 bh = NULL; 149 if (offset) { 150 bh = isofs_bread(inode, block); 151 if (!bh) 152 return 0; 153 memcpy((void *) tmpde + slop, bh->b_data, offset); 154 } 155 de = tmpde; 156 } 157 158 if (first_de) { 159 isofs_normalize_block_and_offset(de, 160 &block_saved, 161 &offset_saved); 162 inode_number = isofs_get_ino(block_saved, 163 offset_saved, 164 bufbits); 165 } 166 167 if (de->flags[-sbi->s_high_sierra] & 0x80) { 168 first_de = 0; 169 filp->f_pos += de_len; 170 continue; 171 } 172 first_de = 1; 173 174 /* Handle the case of the '.' directory */ 175 if (de->name_len[0] == 1 && de->name[0] == 0) { 176 if (filldir(dirent, ".", 1, filp->f_pos, inode->i_ino, DT_DIR) < 0) 177 break; 178 filp->f_pos += de_len; 179 continue; 180 } 181 182 len = 0; 183 184 /* Handle the case of the '..' directory */ 185 if (de->name_len[0] == 1 && de->name[0] == 1) { 186 inode_number = parent_ino(filp->f_path.dentry); 187 if (filldir(dirent, "..", 2, filp->f_pos, inode_number, DT_DIR) < 0) 188 break; 189 filp->f_pos += de_len; 190 continue; 191 } 192 193 /* Handle everything else. Do name translation if there 194 is no Rock Ridge NM field. */ 195 196 /* 197 * Do not report hidden files if so instructed, or associated 198 * files unless instructed to do so 199 */ 200 if ((sbi->s_hide == 'y' && 201 (de->flags[-sbi->s_high_sierra] & 1)) || 202 (sbi->s_showassoc =='n' && 203 (de->flags[-sbi->s_high_sierra] & 4))) { 204 filp->f_pos += de_len; 205 continue; 206 } 207 208 map = 1; 209 if (sbi->s_rock) { 210 len = get_rock_ridge_filename(de, tmpname, inode); 211 if (len != 0) { /* may be -1 */ 212 p = tmpname; 213 map = 0; 214 } 215 } 216 if (map) { 217 #ifdef CONFIG_JOLIET 218 if (sbi->s_joliet_level) { 219 len = get_joliet_filename(de, tmpname, inode); 220 p = tmpname; 221 } else 222 #endif 223 if (sbi->s_mapping == 'a') { 224 len = get_acorn_filename(de, tmpname, inode); 225 p = tmpname; 226 } else 227 if (sbi->s_mapping == 'n') { 228 len = isofs_name_translate(de, tmpname, inode); 229 p = tmpname; 230 } else { 231 p = de->name; 232 len = de->name_len[0]; 233 } 234 } 235 if (len > 0) { 236 if (filldir(dirent, p, len, filp->f_pos, inode_number, DT_UNKNOWN) < 0) 237 break; 238 } 239 filp->f_pos += de_len; 240 241 continue; 242 } 243 if (bh) brelse(bh); 244 return 0; 245 } 246 247 /* 248 * Handle allocation of temporary space for name translation and 249 * handling split directory entries.. The real work is done by 250 * "do_isofs_readdir()". 251 */ 252 static int isofs_readdir(struct file *filp, 253 void *dirent, filldir_t filldir) 254 { 255 int result; 256 char * tmpname; 257 struct iso_directory_record * tmpde; 258 struct inode *inode = filp->f_path.dentry->d_inode; 259 260 tmpname = (char *)__get_free_page(GFP_KERNEL); 261 if (tmpname == NULL) 262 return -ENOMEM; 263 264 lock_kernel(); 265 tmpde = (struct iso_directory_record *) (tmpname+1024); 266 267 result = do_isofs_readdir(inode, filp, dirent, filldir, tmpname, tmpde); 268 269 free_page((unsigned long) tmpname); 270 unlock_kernel(); 271 return result; 272 } 273