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