xref: /openbmc/linux/fs/isofs/namei.c (revision d0034a7a4ac7fae708146ac0059b9c47a1543f0d)
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