162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * linux/fs/isofs/namei.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * (C) 1992 Eric Youngdale Modified for ISO 9660 filesystem. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * (C) 1991 Linus Torvalds - minix filesystem 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/gfp.h> 1162306a36Sopenharmony_ci#include "isofs.h" 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci/* 1462306a36Sopenharmony_ci * ok, we cannot use strncmp, as the name is not in our data space. 1562306a36Sopenharmony_ci * Thus we'll have to use isofs_match. No big problem. Match also makes 1662306a36Sopenharmony_ci * some sanity tests. 1762306a36Sopenharmony_ci */ 1862306a36Sopenharmony_cistatic int 1962306a36Sopenharmony_ciisofs_cmp(struct dentry *dentry, const char *compare, int dlen) 2062306a36Sopenharmony_ci{ 2162306a36Sopenharmony_ci struct qstr qstr; 2262306a36Sopenharmony_ci qstr.name = compare; 2362306a36Sopenharmony_ci qstr.len = dlen; 2462306a36Sopenharmony_ci if (likely(!dentry->d_op)) 2562306a36Sopenharmony_ci return dentry->d_name.len != dlen || memcmp(dentry->d_name.name, compare, dlen); 2662306a36Sopenharmony_ci return dentry->d_op->d_compare(NULL, dentry->d_name.len, dentry->d_name.name, &qstr); 2762306a36Sopenharmony_ci} 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci/* 3062306a36Sopenharmony_ci * isofs_find_entry() 3162306a36Sopenharmony_ci * 3262306a36Sopenharmony_ci * finds an entry in the specified directory with the wanted name. It 3362306a36Sopenharmony_ci * returns the inode number of the found entry, or 0 on error. 3462306a36Sopenharmony_ci */ 3562306a36Sopenharmony_cistatic unsigned long 3662306a36Sopenharmony_ciisofs_find_entry(struct inode *dir, struct dentry *dentry, 3762306a36Sopenharmony_ci unsigned long *block_rv, unsigned long *offset_rv, 3862306a36Sopenharmony_ci char *tmpname, struct iso_directory_record *tmpde) 3962306a36Sopenharmony_ci{ 4062306a36Sopenharmony_ci unsigned long bufsize = ISOFS_BUFFER_SIZE(dir); 4162306a36Sopenharmony_ci unsigned char bufbits = ISOFS_BUFFER_BITS(dir); 4262306a36Sopenharmony_ci unsigned long block, f_pos, offset, block_saved, offset_saved; 4362306a36Sopenharmony_ci struct buffer_head *bh = NULL; 4462306a36Sopenharmony_ci struct isofs_sb_info *sbi = ISOFS_SB(dir->i_sb); 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci if (!ISOFS_I(dir)->i_first_extent) 4762306a36Sopenharmony_ci return 0; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci f_pos = 0; 5062306a36Sopenharmony_ci offset = 0; 5162306a36Sopenharmony_ci block = 0; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci while (f_pos < dir->i_size) { 5462306a36Sopenharmony_ci struct iso_directory_record *de; 5562306a36Sopenharmony_ci int de_len, match, i, dlen; 5662306a36Sopenharmony_ci char *dpnt; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci if (!bh) { 5962306a36Sopenharmony_ci bh = isofs_bread(dir, block); 6062306a36Sopenharmony_ci if (!bh) 6162306a36Sopenharmony_ci return 0; 6262306a36Sopenharmony_ci } 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci de = (struct iso_directory_record *) (bh->b_data + offset); 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci de_len = *(unsigned char *) de; 6762306a36Sopenharmony_ci if (!de_len) { 6862306a36Sopenharmony_ci brelse(bh); 6962306a36Sopenharmony_ci bh = NULL; 7062306a36Sopenharmony_ci f_pos = (f_pos + ISOFS_BLOCK_SIZE) & ~(ISOFS_BLOCK_SIZE - 1); 7162306a36Sopenharmony_ci block = f_pos >> bufbits; 7262306a36Sopenharmony_ci offset = 0; 7362306a36Sopenharmony_ci continue; 7462306a36Sopenharmony_ci } 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci block_saved = bh->b_blocknr; 7762306a36Sopenharmony_ci offset_saved = offset; 7862306a36Sopenharmony_ci offset += de_len; 7962306a36Sopenharmony_ci f_pos += de_len; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci /* Make sure we have a full directory entry */ 8262306a36Sopenharmony_ci if (offset >= bufsize) { 8362306a36Sopenharmony_ci int slop = bufsize - offset + de_len; 8462306a36Sopenharmony_ci memcpy(tmpde, de, slop); 8562306a36Sopenharmony_ci offset &= bufsize - 1; 8662306a36Sopenharmony_ci block++; 8762306a36Sopenharmony_ci brelse(bh); 8862306a36Sopenharmony_ci bh = NULL; 8962306a36Sopenharmony_ci if (offset) { 9062306a36Sopenharmony_ci bh = isofs_bread(dir, block); 9162306a36Sopenharmony_ci if (!bh) 9262306a36Sopenharmony_ci return 0; 9362306a36Sopenharmony_ci memcpy((void *) tmpde + slop, bh->b_data, offset); 9462306a36Sopenharmony_ci } 9562306a36Sopenharmony_ci de = tmpde; 9662306a36Sopenharmony_ci } 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci dlen = de->name_len[0]; 9962306a36Sopenharmony_ci dpnt = de->name; 10062306a36Sopenharmony_ci /* Basic sanity check, whether name doesn't exceed dir entry */ 10162306a36Sopenharmony_ci if (de_len < dlen + sizeof(struct iso_directory_record)) { 10262306a36Sopenharmony_ci printk(KERN_NOTICE "iso9660: Corrupted directory entry" 10362306a36Sopenharmony_ci " in block %lu of inode %lu\n", block, 10462306a36Sopenharmony_ci dir->i_ino); 10562306a36Sopenharmony_ci brelse(bh); 10662306a36Sopenharmony_ci return 0; 10762306a36Sopenharmony_ci } 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci if (sbi->s_rock && 11062306a36Sopenharmony_ci ((i = get_rock_ridge_filename(de, tmpname, dir)))) { 11162306a36Sopenharmony_ci dlen = i; /* possibly -1 */ 11262306a36Sopenharmony_ci dpnt = tmpname; 11362306a36Sopenharmony_ci#ifdef CONFIG_JOLIET 11462306a36Sopenharmony_ci } else if (sbi->s_joliet_level) { 11562306a36Sopenharmony_ci dlen = get_joliet_filename(de, tmpname, dir); 11662306a36Sopenharmony_ci dpnt = tmpname; 11762306a36Sopenharmony_ci#endif 11862306a36Sopenharmony_ci } else if (sbi->s_mapping == 'a') { 11962306a36Sopenharmony_ci dlen = get_acorn_filename(de, tmpname, dir); 12062306a36Sopenharmony_ci dpnt = tmpname; 12162306a36Sopenharmony_ci } else if (sbi->s_mapping == 'n') { 12262306a36Sopenharmony_ci dlen = isofs_name_translate(de, tmpname, dir); 12362306a36Sopenharmony_ci dpnt = tmpname; 12462306a36Sopenharmony_ci } 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci /* 12762306a36Sopenharmony_ci * Skip hidden or associated files unless hide or showassoc, 12862306a36Sopenharmony_ci * respectively, is set 12962306a36Sopenharmony_ci */ 13062306a36Sopenharmony_ci match = 0; 13162306a36Sopenharmony_ci if (dlen > 0 && 13262306a36Sopenharmony_ci (!sbi->s_hide || 13362306a36Sopenharmony_ci (!(de->flags[-sbi->s_high_sierra] & 1))) && 13462306a36Sopenharmony_ci (sbi->s_showassoc || 13562306a36Sopenharmony_ci (!(de->flags[-sbi->s_high_sierra] & 4)))) { 13662306a36Sopenharmony_ci if (dpnt && (dlen > 1 || dpnt[0] > 1)) 13762306a36Sopenharmony_ci match = (isofs_cmp(dentry, dpnt, dlen) == 0); 13862306a36Sopenharmony_ci } 13962306a36Sopenharmony_ci if (match) { 14062306a36Sopenharmony_ci isofs_normalize_block_and_offset(de, 14162306a36Sopenharmony_ci &block_saved, 14262306a36Sopenharmony_ci &offset_saved); 14362306a36Sopenharmony_ci *block_rv = block_saved; 14462306a36Sopenharmony_ci *offset_rv = offset_saved; 14562306a36Sopenharmony_ci brelse(bh); 14662306a36Sopenharmony_ci return 1; 14762306a36Sopenharmony_ci } 14862306a36Sopenharmony_ci } 14962306a36Sopenharmony_ci brelse(bh); 15062306a36Sopenharmony_ci return 0; 15162306a36Sopenharmony_ci} 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_cistruct dentry *isofs_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags) 15462306a36Sopenharmony_ci{ 15562306a36Sopenharmony_ci int found; 15662306a36Sopenharmony_ci unsigned long block; 15762306a36Sopenharmony_ci unsigned long offset; 15862306a36Sopenharmony_ci struct inode *inode; 15962306a36Sopenharmony_ci struct page *page; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci page = alloc_page(GFP_USER); 16262306a36Sopenharmony_ci if (!page) 16362306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci found = isofs_find_entry(dir, dentry, 16662306a36Sopenharmony_ci &block, &offset, 16762306a36Sopenharmony_ci page_address(page), 16862306a36Sopenharmony_ci 1024 + page_address(page)); 16962306a36Sopenharmony_ci __free_page(page); 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci inode = found ? isofs_iget(dir->i_sb, block, offset) : NULL; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci return d_splice_alias(inode, dentry); 17462306a36Sopenharmony_ci} 175