1 /* 2 * linux/fs/isofs/namei.c 3 * 4 * (C) 1992 Eric Youngdale Modified for ISO 9660 filesystem. 5 * 6 * (C) 1991 Linus Torvalds - minix filesystem 7 */ 8 9 #include <linux/gfp.h> 10 #include "isofs.h" 11 12 /* 13 * ok, we cannot use strncmp, as the name is not in our data space. 14 * Thus we'll have to use isofs_match. No big problem. Match also makes 15 * some sanity tests. 16 */ 17 static int 18 isofs_cmp(struct dentry *dentry, const char *compare, int dlen) 19 { 20 struct qstr qstr; 21 qstr.name = compare; 22 qstr.len = dlen; 23 return dentry->d_op->d_compare(NULL, NULL, dentry->d_name.len, dentry->d_name.name, &qstr); 24 } 25 26 /* 27 * isofs_find_entry() 28 * 29 * finds an entry in the specified directory with the wanted name. It 30 * returns the inode number of the found entry, or 0 on error. 31 */ 32 static unsigned long 33 isofs_find_entry(struct inode *dir, struct dentry *dentry, 34 unsigned long *block_rv, unsigned long *offset_rv, 35 char *tmpname, struct iso_directory_record *tmpde) 36 { 37 unsigned long bufsize = ISOFS_BUFFER_SIZE(dir); 38 unsigned char bufbits = ISOFS_BUFFER_BITS(dir); 39 unsigned long block, f_pos, offset, block_saved, offset_saved; 40 struct buffer_head *bh = NULL; 41 struct isofs_sb_info *sbi = ISOFS_SB(dir->i_sb); 42 43 if (!ISOFS_I(dir)->i_first_extent) 44 return 0; 45 46 f_pos = 0; 47 offset = 0; 48 block = 0; 49 50 while (f_pos < dir->i_size) { 51 struct iso_directory_record *de; 52 int de_len, match, i, dlen; 53 char *dpnt; 54 55 if (!bh) { 56 bh = isofs_bread(dir, block); 57 if (!bh) 58 return 0; 59 } 60 61 de = (struct iso_directory_record *) (bh->b_data + offset); 62 63 de_len = *(unsigned char *) de; 64 if (!de_len) { 65 brelse(bh); 66 bh = NULL; 67 f_pos = (f_pos + ISOFS_BLOCK_SIZE) & ~(ISOFS_BLOCK_SIZE - 1); 68 block = f_pos >> bufbits; 69 offset = 0; 70 continue; 71 } 72 73 block_saved = bh->b_blocknr; 74 offset_saved = offset; 75 offset += de_len; 76 f_pos += de_len; 77 78 /* Make sure we have a full directory entry */ 79 if (offset >= bufsize) { 80 int slop = bufsize - offset + de_len; 81 memcpy(tmpde, de, slop); 82 offset &= bufsize - 1; 83 block++; 84 brelse(bh); 85 bh = NULL; 86 if (offset) { 87 bh = isofs_bread(dir, block); 88 if (!bh) 89 return 0; 90 memcpy((void *) tmpde + slop, bh->b_data, offset); 91 } 92 de = tmpde; 93 } 94 95 dlen = de->name_len[0]; 96 dpnt = de->name; 97 /* Basic sanity check, whether name doesn't exceed dir entry */ 98 if (de_len < dlen + sizeof(struct iso_directory_record)) { 99 printk(KERN_NOTICE "iso9660: Corrupted directory entry" 100 " in block %lu of inode %lu\n", block, 101 dir->i_ino); 102 return 0; 103 } 104 105 if (sbi->s_rock && 106 ((i = get_rock_ridge_filename(de, tmpname, dir)))) { 107 dlen = i; /* possibly -1 */ 108 dpnt = tmpname; 109 #ifdef CONFIG_JOLIET 110 } else if (sbi->s_joliet_level) { 111 dlen = get_joliet_filename(de, tmpname, dir); 112 dpnt = tmpname; 113 #endif 114 } else if (sbi->s_mapping == 'a') { 115 dlen = get_acorn_filename(de, tmpname, dir); 116 dpnt = tmpname; 117 } else if (sbi->s_mapping == 'n') { 118 dlen = isofs_name_translate(de, tmpname, dir); 119 dpnt = tmpname; 120 } 121 122 /* 123 * Skip hidden or associated files unless hide or showassoc, 124 * respectively, is set 125 */ 126 match = 0; 127 if (dlen > 0 && 128 (!sbi->s_hide || 129 (!(de->flags[-sbi->s_high_sierra] & 1))) && 130 (sbi->s_showassoc || 131 (!(de->flags[-sbi->s_high_sierra] & 4)))) { 132 if (dpnt && (dlen > 1 || dpnt[0] > 1)) 133 match = (isofs_cmp(dentry, dpnt, dlen) == 0); 134 } 135 if (match) { 136 isofs_normalize_block_and_offset(de, 137 &block_saved, 138 &offset_saved); 139 *block_rv = block_saved; 140 *offset_rv = offset_saved; 141 brelse(bh); 142 return 1; 143 } 144 } 145 brelse(bh); 146 return 0; 147 } 148 149 struct dentry *isofs_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags) 150 { 151 int found; 152 unsigned long uninitialized_var(block); 153 unsigned long uninitialized_var(offset); 154 struct inode *inode; 155 struct page *page; 156 157 page = alloc_page(GFP_USER); 158 if (!page) 159 return ERR_PTR(-ENOMEM); 160 161 found = isofs_find_entry(dir, dentry, 162 &block, &offset, 163 page_address(page), 164 1024 + page_address(page)); 165 __free_page(page); 166 167 inode = found ? isofs_iget(dir->i_sb, block, offset) : NULL; 168 169 return d_splice_alias(inode, dentry); 170 } 171