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/time.h> 10 #include <linux/iso_fs.h> 11 #include <linux/kernel.h> 12 #include <linux/string.h> 13 #include <linux/stat.h> 14 #include <linux/fcntl.h> 15 #include <linux/mm.h> 16 #include <linux/errno.h> 17 #include <linux/config.h> /* Joliet? */ 18 #include <linux/smp_lock.h> 19 #include <linux/buffer_head.h> 20 #include <linux/dcache.h> 21 22 #include <asm/uaccess.h> 23 24 /* 25 * ok, we cannot use strncmp, as the name is not in our data space. 26 * Thus we'll have to use isofs_match. No big problem. Match also makes 27 * some sanity tests. 28 */ 29 static int 30 isofs_cmp(struct dentry * dentry, const char * compare, int dlen) 31 { 32 struct qstr qstr; 33 34 if (!compare) 35 return 1; 36 37 /* check special "." and ".." files */ 38 if (dlen == 1) { 39 /* "." */ 40 if (compare[0] == 0) { 41 if (!dentry->d_name.len) 42 return 0; 43 compare = "."; 44 } else if (compare[0] == 1) { 45 compare = ".."; 46 dlen = 2; 47 } 48 } 49 50 qstr.name = compare; 51 qstr.len = dlen; 52 return dentry->d_op->d_compare(dentry, &dentry->d_name, &qstr); 53 } 54 55 /* 56 * isofs_find_entry() 57 * 58 * finds an entry in the specified directory with the wanted name. It 59 * returns the inode number of the found entry, or 0 on error. 60 */ 61 static unsigned long 62 isofs_find_entry(struct inode *dir, struct dentry *dentry, 63 unsigned long *block_rv, unsigned long* offset_rv, 64 char * tmpname, struct iso_directory_record * tmpde) 65 { 66 unsigned long bufsize = ISOFS_BUFFER_SIZE(dir); 67 unsigned char bufbits = ISOFS_BUFFER_BITS(dir); 68 unsigned long block, f_pos, offset, block_saved, offset_saved; 69 struct buffer_head * bh = NULL; 70 struct isofs_sb_info *sbi = ISOFS_SB(dir->i_sb); 71 72 if (!ISOFS_I(dir)->i_first_extent) 73 return 0; 74 75 f_pos = 0; 76 offset = 0; 77 block = 0; 78 79 while (f_pos < dir->i_size) { 80 struct iso_directory_record * de; 81 int de_len, match, i, dlen; 82 char *dpnt; 83 84 if (!bh) { 85 bh = isofs_bread(dir, block); 86 if (!bh) 87 return 0; 88 } 89 90 de = (struct iso_directory_record *) (bh->b_data + offset); 91 92 de_len = *(unsigned char *) de; 93 if (!de_len) { 94 brelse(bh); 95 bh = NULL; 96 f_pos = (f_pos + ISOFS_BLOCK_SIZE) & ~(ISOFS_BLOCK_SIZE - 1); 97 block = f_pos >> bufbits; 98 offset = 0; 99 continue; 100 } 101 102 block_saved = bh->b_blocknr; 103 offset_saved = offset; 104 offset += de_len; 105 f_pos += de_len; 106 107 /* Make sure we have a full directory entry */ 108 if (offset >= bufsize) { 109 int slop = bufsize - offset + de_len; 110 memcpy(tmpde, de, slop); 111 offset &= bufsize - 1; 112 block++; 113 brelse(bh); 114 bh = NULL; 115 if (offset) { 116 bh = isofs_bread(dir, block); 117 if (!bh) 118 return 0; 119 memcpy((void *) tmpde + slop, bh->b_data, offset); 120 } 121 de = tmpde; 122 } 123 124 dlen = de->name_len[0]; 125 dpnt = de->name; 126 127 if (sbi->s_rock && 128 ((i = get_rock_ridge_filename(de, tmpname, dir)))) { 129 dlen = i; /* possibly -1 */ 130 dpnt = tmpname; 131 #ifdef CONFIG_JOLIET 132 } else if (sbi->s_joliet_level) { 133 dlen = get_joliet_filename(de, tmpname, dir); 134 dpnt = tmpname; 135 #endif 136 } else if (sbi->s_mapping == 'a') { 137 dlen = get_acorn_filename(de, tmpname, dir); 138 dpnt = tmpname; 139 } else if (sbi->s_mapping == 'n') { 140 dlen = isofs_name_translate(de, tmpname, dir); 141 dpnt = tmpname; 142 } 143 144 /* 145 * Skip hidden or associated files unless unhide is set 146 */ 147 match = 0; 148 if (dlen > 0 && 149 (!(de->flags[-sbi->s_high_sierra] & 5) 150 || sbi->s_unhide == 'y')) 151 { 152 match = (isofs_cmp(dentry,dpnt,dlen) == 0); 153 } 154 if (match) { 155 isofs_normalize_block_and_offset(de, 156 &block_saved, 157 &offset_saved); 158 *block_rv = block_saved; 159 *offset_rv = offset_saved; 160 if (bh) brelse(bh); 161 return 1; 162 } 163 } 164 if (bh) brelse(bh); 165 return 0; 166 } 167 168 struct dentry *isofs_lookup(struct inode * dir, struct dentry * dentry, struct nameidata *nd) 169 { 170 int found; 171 unsigned long block, offset; 172 struct inode *inode; 173 struct page *page; 174 175 dentry->d_op = dir->i_sb->s_root->d_op; 176 177 page = alloc_page(GFP_USER); 178 if (!page) 179 return ERR_PTR(-ENOMEM); 180 181 lock_kernel(); 182 found = isofs_find_entry(dir, dentry, 183 &block, &offset, 184 page_address(page), 185 1024 + page_address(page)); 186 __free_page(page); 187 188 inode = NULL; 189 if (found) { 190 inode = isofs_iget(dir->i_sb, block, offset); 191 if (!inode) { 192 unlock_kernel(); 193 return ERR_PTR(-EACCES); 194 } 195 } 196 unlock_kernel(); 197 if (inode) 198 return d_splice_alias(inode, dentry); 199 d_add(dentry, inode); 200 return NULL; 201 } 202