1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds * linux/fs/isofs/namei.c
41da177e4SLinus Torvalds *
51da177e4SLinus Torvalds * (C) 1992 Eric Youngdale Modified for ISO 9660 filesystem.
61da177e4SLinus Torvalds *
71da177e4SLinus Torvalds * (C) 1991 Linus Torvalds - minix filesystem
81da177e4SLinus Torvalds */
91da177e4SLinus Torvalds
105a0e3ad6STejun Heo #include <linux/gfp.h>
1194f2f715SAl Viro #include "isofs.h"
121da177e4SLinus Torvalds
131da177e4SLinus Torvalds /*
141da177e4SLinus Torvalds * ok, we cannot use strncmp, as the name is not in our data space.
151da177e4SLinus Torvalds * Thus we'll have to use isofs_match. No big problem. Match also makes
161da177e4SLinus Torvalds * some sanity tests.
171da177e4SLinus Torvalds */
181da177e4SLinus Torvalds static int
isofs_cmp(struct dentry * dentry,const char * compare,int dlen)191da177e4SLinus Torvalds isofs_cmp(struct dentry *dentry, const char *compare, int dlen)
201da177e4SLinus Torvalds {
211da177e4SLinus Torvalds struct qstr qstr;
221da177e4SLinus Torvalds qstr.name = compare;
231da177e4SLinus Torvalds qstr.len = dlen;
24b0afd8e5SAl Viro if (likely(!dentry->d_op))
25b0afd8e5SAl Viro return dentry->d_name.len != dlen || memcmp(dentry->d_name.name, compare, dlen);
266fa67e70SAl Viro return dentry->d_op->d_compare(NULL, dentry->d_name.len, dentry->d_name.name, &qstr);
271da177e4SLinus Torvalds }
281da177e4SLinus Torvalds
291da177e4SLinus Torvalds /*
301da177e4SLinus Torvalds * isofs_find_entry()
311da177e4SLinus Torvalds *
321da177e4SLinus Torvalds * finds an entry in the specified directory with the wanted name. It
331da177e4SLinus Torvalds * returns the inode number of the found entry, or 0 on error.
341da177e4SLinus Torvalds */
351da177e4SLinus Torvalds static unsigned long
isofs_find_entry(struct inode * dir,struct dentry * dentry,unsigned long * block_rv,unsigned long * offset_rv,char * tmpname,struct iso_directory_record * tmpde)361da177e4SLinus Torvalds isofs_find_entry(struct inode *dir, struct dentry *dentry,
371da177e4SLinus Torvalds unsigned long *block_rv, unsigned long *offset_rv,
381da177e4SLinus Torvalds char *tmpname, struct iso_directory_record *tmpde)
391da177e4SLinus Torvalds {
401da177e4SLinus Torvalds unsigned long bufsize = ISOFS_BUFFER_SIZE(dir);
411da177e4SLinus Torvalds unsigned char bufbits = ISOFS_BUFFER_BITS(dir);
421da177e4SLinus Torvalds unsigned long block, f_pos, offset, block_saved, offset_saved;
431da177e4SLinus Torvalds struct buffer_head *bh = NULL;
441da177e4SLinus Torvalds struct isofs_sb_info *sbi = ISOFS_SB(dir->i_sb);
451da177e4SLinus Torvalds
461da177e4SLinus Torvalds if (!ISOFS_I(dir)->i_first_extent)
471da177e4SLinus Torvalds return 0;
481da177e4SLinus Torvalds
491da177e4SLinus Torvalds f_pos = 0;
501da177e4SLinus Torvalds offset = 0;
511da177e4SLinus Torvalds block = 0;
521da177e4SLinus Torvalds
531da177e4SLinus Torvalds while (f_pos < dir->i_size) {
541da177e4SLinus Torvalds struct iso_directory_record *de;
551da177e4SLinus Torvalds int de_len, match, i, dlen;
561da177e4SLinus Torvalds char *dpnt;
571da177e4SLinus Torvalds
581da177e4SLinus Torvalds if (!bh) {
591da177e4SLinus Torvalds bh = isofs_bread(dir, block);
601da177e4SLinus Torvalds if (!bh)
611da177e4SLinus Torvalds return 0;
621da177e4SLinus Torvalds }
631da177e4SLinus Torvalds
641da177e4SLinus Torvalds de = (struct iso_directory_record *) (bh->b_data + offset);
651da177e4SLinus Torvalds
661da177e4SLinus Torvalds de_len = *(unsigned char *) de;
671da177e4SLinus Torvalds if (!de_len) {
681da177e4SLinus Torvalds brelse(bh);
691da177e4SLinus Torvalds bh = NULL;
701da177e4SLinus Torvalds f_pos = (f_pos + ISOFS_BLOCK_SIZE) & ~(ISOFS_BLOCK_SIZE - 1);
711da177e4SLinus Torvalds block = f_pos >> bufbits;
721da177e4SLinus Torvalds offset = 0;
731da177e4SLinus Torvalds continue;
741da177e4SLinus Torvalds }
751da177e4SLinus Torvalds
761da177e4SLinus Torvalds block_saved = bh->b_blocknr;
771da177e4SLinus Torvalds offset_saved = offset;
781da177e4SLinus Torvalds offset += de_len;
791da177e4SLinus Torvalds f_pos += de_len;
801da177e4SLinus Torvalds
811da177e4SLinus Torvalds /* Make sure we have a full directory entry */
821da177e4SLinus Torvalds if (offset >= bufsize) {
831da177e4SLinus Torvalds int slop = bufsize - offset + de_len;
841da177e4SLinus Torvalds memcpy(tmpde, de, slop);
851da177e4SLinus Torvalds offset &= bufsize - 1;
861da177e4SLinus Torvalds block++;
871da177e4SLinus Torvalds brelse(bh);
881da177e4SLinus Torvalds bh = NULL;
891da177e4SLinus Torvalds if (offset) {
901da177e4SLinus Torvalds bh = isofs_bread(dir, block);
911da177e4SLinus Torvalds if (!bh)
921da177e4SLinus Torvalds return 0;
931da177e4SLinus Torvalds memcpy((void *) tmpde + slop, bh->b_data, offset);
941da177e4SLinus Torvalds }
951da177e4SLinus Torvalds de = tmpde;
961da177e4SLinus Torvalds }
971da177e4SLinus Torvalds
981da177e4SLinus Torvalds dlen = de->name_len[0];
991da177e4SLinus Torvalds dpnt = de->name;
1002deb1accSJan Kara /* Basic sanity check, whether name doesn't exceed dir entry */
1012deb1accSJan Kara if (de_len < dlen + sizeof(struct iso_directory_record)) {
1022deb1accSJan Kara printk(KERN_NOTICE "iso9660: Corrupted directory entry"
1032deb1accSJan Kara " in block %lu of inode %lu\n", block,
1042deb1accSJan Kara dir->i_ino);
105*0a6dc67aSPan Bian brelse(bh);
1062deb1accSJan Kara return 0;
1072deb1accSJan Kara }
1081da177e4SLinus Torvalds
1091da177e4SLinus Torvalds if (sbi->s_rock &&
1101da177e4SLinus Torvalds ((i = get_rock_ridge_filename(de, tmpname, dir)))) {
1111da177e4SLinus Torvalds dlen = i; /* possibly -1 */
1121da177e4SLinus Torvalds dpnt = tmpname;
1131da177e4SLinus Torvalds #ifdef CONFIG_JOLIET
1141da177e4SLinus Torvalds } else if (sbi->s_joliet_level) {
1151da177e4SLinus Torvalds dlen = get_joliet_filename(de, tmpname, dir);
1161da177e4SLinus Torvalds dpnt = tmpname;
1171da177e4SLinus Torvalds #endif
1181da177e4SLinus Torvalds } else if (sbi->s_mapping == 'a') {
1191da177e4SLinus Torvalds dlen = get_acorn_filename(de, tmpname, dir);
1201da177e4SLinus Torvalds dpnt = tmpname;
1211da177e4SLinus Torvalds } else if (sbi->s_mapping == 'n') {
1221da177e4SLinus Torvalds dlen = isofs_name_translate(de, tmpname, dir);
1231da177e4SLinus Torvalds dpnt = tmpname;
1241da177e4SLinus Torvalds }
1251da177e4SLinus Torvalds
1261da177e4SLinus Torvalds /*
1279769f4ebSJeremy White * Skip hidden or associated files unless hide or showassoc,
1289769f4ebSJeremy White * respectively, is set
1291da177e4SLinus Torvalds */
1301da177e4SLinus Torvalds match = 0;
1311da177e4SLinus Torvalds if (dlen > 0 &&
1325404ac8eSJan Kara (!sbi->s_hide ||
1339769f4ebSJeremy White (!(de->flags[-sbi->s_high_sierra] & 1))) &&
1345404ac8eSJan Kara (sbi->s_showassoc ||
1359769f4ebSJeremy White (!(de->flags[-sbi->s_high_sierra] & 4)))) {
136f643ff55SAl Viro if (dpnt && (dlen > 1 || dpnt[0] > 1))
1371da177e4SLinus Torvalds match = (isofs_cmp(dentry, dpnt, dlen) == 0);
1381da177e4SLinus Torvalds }
1391da177e4SLinus Torvalds if (match) {
1401da177e4SLinus Torvalds isofs_normalize_block_and_offset(de,
1411da177e4SLinus Torvalds &block_saved,
1421da177e4SLinus Torvalds &offset_saved);
1431da177e4SLinus Torvalds *block_rv = block_saved;
1441da177e4SLinus Torvalds *offset_rv = offset_saved;
1459769f4ebSJeremy White brelse(bh);
1461da177e4SLinus Torvalds return 1;
1471da177e4SLinus Torvalds }
1481da177e4SLinus Torvalds }
1499769f4ebSJeremy White brelse(bh);
1501da177e4SLinus Torvalds return 0;
1511da177e4SLinus Torvalds }
1521da177e4SLinus Torvalds
isofs_lookup(struct inode * dir,struct dentry * dentry,unsigned int flags)15300cd8dd3SAl Viro struct dentry *isofs_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags)
1541da177e4SLinus Torvalds {
1551da177e4SLinus Torvalds int found;
1563f649ab7SKees Cook unsigned long block;
1573f649ab7SKees Cook unsigned long offset;
1581da177e4SLinus Torvalds struct inode *inode;
1591da177e4SLinus Torvalds struct page *page;
1601da177e4SLinus Torvalds
1611da177e4SLinus Torvalds page = alloc_page(GFP_USER);
1621da177e4SLinus Torvalds if (!page)
1631da177e4SLinus Torvalds return ERR_PTR(-ENOMEM);
1641da177e4SLinus Torvalds
1651da177e4SLinus Torvalds found = isofs_find_entry(dir, dentry,
1661da177e4SLinus Torvalds &block, &offset,
1671da177e4SLinus Torvalds page_address(page),
1681da177e4SLinus Torvalds 1024 + page_address(page));
1691da177e4SLinus Torvalds __free_page(page);
1701da177e4SLinus Torvalds
171a9049376SAl Viro inode = found ? isofs_iget(dir->i_sb, block, offset) : NULL;
172a9049376SAl Viro
1731da177e4SLinus Torvalds return d_splice_alias(inode, dentry);
1741da177e4SLinus Torvalds }
175