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