xref: /openbmc/linux/fs/isofs/namei.c (revision a9049376ee05bf966bfe2b081b5071326856890a)
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 
221da177e4SLinus Torvalds 	if (!compare)
231da177e4SLinus Torvalds 		return 1;
241da177e4SLinus Torvalds 
251da177e4SLinus Torvalds 	/* check special "." and ".." files */
261da177e4SLinus Torvalds 	if (dlen == 1) {
271da177e4SLinus Torvalds 		/* "." */
281da177e4SLinus Torvalds 		if (compare[0] == 0) {
291da177e4SLinus Torvalds 			if (!dentry->d_name.len)
301da177e4SLinus Torvalds 				return 0;
311da177e4SLinus Torvalds 			compare = ".";
321da177e4SLinus Torvalds 		} else if (compare[0] == 1) {
331da177e4SLinus Torvalds 			compare = "..";
341da177e4SLinus Torvalds 			dlen = 2;
351da177e4SLinus Torvalds 		}
361da177e4SLinus Torvalds 	}
371da177e4SLinus Torvalds 
381da177e4SLinus Torvalds 	qstr.name = compare;
391da177e4SLinus Torvalds 	qstr.len = dlen;
40621e155aSNick Piggin 	return dentry->d_op->d_compare(NULL, NULL, NULL, NULL,
41621e155aSNick Piggin 			dentry->d_name.len, dentry->d_name.name, &qstr);
421da177e4SLinus Torvalds }
431da177e4SLinus Torvalds 
441da177e4SLinus Torvalds /*
451da177e4SLinus Torvalds  *	isofs_find_entry()
461da177e4SLinus Torvalds  *
471da177e4SLinus Torvalds  * finds an entry in the specified directory with the wanted name. It
481da177e4SLinus Torvalds  * returns the inode number of the found entry, or 0 on error.
491da177e4SLinus Torvalds  */
501da177e4SLinus Torvalds static unsigned long
511da177e4SLinus Torvalds isofs_find_entry(struct inode *dir, struct dentry *dentry,
521da177e4SLinus Torvalds 	unsigned long *block_rv, unsigned long *offset_rv,
531da177e4SLinus Torvalds 	char *tmpname, struct iso_directory_record *tmpde)
541da177e4SLinus Torvalds {
551da177e4SLinus Torvalds 	unsigned long bufsize = ISOFS_BUFFER_SIZE(dir);
561da177e4SLinus Torvalds 	unsigned char bufbits = ISOFS_BUFFER_BITS(dir);
571da177e4SLinus Torvalds 	unsigned long block, f_pos, offset, block_saved, offset_saved;
581da177e4SLinus Torvalds 	struct buffer_head *bh = NULL;
591da177e4SLinus Torvalds 	struct isofs_sb_info *sbi = ISOFS_SB(dir->i_sb);
601da177e4SLinus Torvalds 
611da177e4SLinus Torvalds 	if (!ISOFS_I(dir)->i_first_extent)
621da177e4SLinus Torvalds 		return 0;
631da177e4SLinus Torvalds 
641da177e4SLinus Torvalds 	f_pos = 0;
651da177e4SLinus Torvalds 	offset = 0;
661da177e4SLinus Torvalds 	block = 0;
671da177e4SLinus Torvalds 
681da177e4SLinus Torvalds 	while (f_pos < dir->i_size) {
691da177e4SLinus Torvalds 		struct iso_directory_record *de;
701da177e4SLinus Torvalds 		int de_len, match, i, dlen;
711da177e4SLinus Torvalds 		char *dpnt;
721da177e4SLinus Torvalds 
731da177e4SLinus Torvalds 		if (!bh) {
741da177e4SLinus Torvalds 			bh = isofs_bread(dir, block);
751da177e4SLinus Torvalds 			if (!bh)
761da177e4SLinus Torvalds 				return 0;
771da177e4SLinus Torvalds 		}
781da177e4SLinus Torvalds 
791da177e4SLinus Torvalds 		de = (struct iso_directory_record *) (bh->b_data + offset);
801da177e4SLinus Torvalds 
811da177e4SLinus Torvalds 		de_len = *(unsigned char *) de;
821da177e4SLinus Torvalds 		if (!de_len) {
831da177e4SLinus Torvalds 			brelse(bh);
841da177e4SLinus Torvalds 			bh = NULL;
851da177e4SLinus Torvalds 			f_pos = (f_pos + ISOFS_BLOCK_SIZE) & ~(ISOFS_BLOCK_SIZE - 1);
861da177e4SLinus Torvalds 			block = f_pos >> bufbits;
871da177e4SLinus Torvalds 			offset = 0;
881da177e4SLinus Torvalds 			continue;
891da177e4SLinus Torvalds 		}
901da177e4SLinus Torvalds 
911da177e4SLinus Torvalds 		block_saved = bh->b_blocknr;
921da177e4SLinus Torvalds 		offset_saved = offset;
931da177e4SLinus Torvalds 		offset += de_len;
941da177e4SLinus Torvalds 		f_pos += de_len;
951da177e4SLinus Torvalds 
961da177e4SLinus Torvalds 		/* Make sure we have a full directory entry */
971da177e4SLinus Torvalds 		if (offset >= bufsize) {
981da177e4SLinus Torvalds 			int slop = bufsize - offset + de_len;
991da177e4SLinus Torvalds 			memcpy(tmpde, de, slop);
1001da177e4SLinus Torvalds 			offset &= bufsize - 1;
1011da177e4SLinus Torvalds 			block++;
1021da177e4SLinus Torvalds 			brelse(bh);
1031da177e4SLinus Torvalds 			bh = NULL;
1041da177e4SLinus Torvalds 			if (offset) {
1051da177e4SLinus Torvalds 				bh = isofs_bread(dir, block);
1061da177e4SLinus Torvalds 				if (!bh)
1071da177e4SLinus Torvalds 					return 0;
1081da177e4SLinus Torvalds 				memcpy((void *) tmpde + slop, bh->b_data, offset);
1091da177e4SLinus Torvalds 			}
1101da177e4SLinus Torvalds 			de = tmpde;
1111da177e4SLinus Torvalds 		}
1121da177e4SLinus Torvalds 
1131da177e4SLinus Torvalds 		dlen = de->name_len[0];
1141da177e4SLinus Torvalds 		dpnt = de->name;
1152deb1accSJan Kara 		/* Basic sanity check, whether name doesn't exceed dir entry */
1162deb1accSJan Kara 		if (de_len < dlen + sizeof(struct iso_directory_record)) {
1172deb1accSJan Kara 			printk(KERN_NOTICE "iso9660: Corrupted directory entry"
1182deb1accSJan Kara 			       " in block %lu of inode %lu\n", block,
1192deb1accSJan Kara 			       dir->i_ino);
1202deb1accSJan Kara 			return 0;
1212deb1accSJan Kara 		}
1221da177e4SLinus Torvalds 
1231da177e4SLinus Torvalds 		if (sbi->s_rock &&
1241da177e4SLinus Torvalds 		    ((i = get_rock_ridge_filename(de, tmpname, dir)))) {
1251da177e4SLinus Torvalds 			dlen = i;	/* possibly -1 */
1261da177e4SLinus Torvalds 			dpnt = tmpname;
1271da177e4SLinus Torvalds #ifdef CONFIG_JOLIET
1281da177e4SLinus Torvalds 		} else if (sbi->s_joliet_level) {
1291da177e4SLinus Torvalds 			dlen = get_joliet_filename(de, tmpname, dir);
1301da177e4SLinus Torvalds 			dpnt = tmpname;
1311da177e4SLinus Torvalds #endif
1321da177e4SLinus Torvalds 		} else if (sbi->s_mapping == 'a') {
1331da177e4SLinus Torvalds 			dlen = get_acorn_filename(de, tmpname, dir);
1341da177e4SLinus Torvalds 			dpnt = tmpname;
1351da177e4SLinus Torvalds 		} else if (sbi->s_mapping == 'n') {
1361da177e4SLinus Torvalds 			dlen = isofs_name_translate(de, tmpname, dir);
1371da177e4SLinus Torvalds 			dpnt = tmpname;
1381da177e4SLinus Torvalds 		}
1391da177e4SLinus Torvalds 
1401da177e4SLinus Torvalds 		/*
1419769f4ebSJeremy White 		 * Skip hidden or associated files unless hide or showassoc,
1429769f4ebSJeremy White 		 * respectively, is set
1431da177e4SLinus Torvalds 		 */
1441da177e4SLinus Torvalds 		match = 0;
1451da177e4SLinus Torvalds 		if (dlen > 0 &&
1465404ac8eSJan Kara 			(!sbi->s_hide ||
1479769f4ebSJeremy White 				(!(de->flags[-sbi->s_high_sierra] & 1))) &&
1485404ac8eSJan Kara 			(sbi->s_showassoc ||
1499769f4ebSJeremy White 				(!(de->flags[-sbi->s_high_sierra] & 4)))) {
1501da177e4SLinus Torvalds 			match = (isofs_cmp(dentry, dpnt, dlen) == 0);
1511da177e4SLinus Torvalds 		}
1521da177e4SLinus Torvalds 		if (match) {
1531da177e4SLinus Torvalds 			isofs_normalize_block_and_offset(de,
1541da177e4SLinus Torvalds 							 &block_saved,
1551da177e4SLinus Torvalds 							 &offset_saved);
1561da177e4SLinus Torvalds 			*block_rv = block_saved;
1571da177e4SLinus Torvalds 			*offset_rv = offset_saved;
1589769f4ebSJeremy White 			brelse(bh);
1591da177e4SLinus Torvalds 			return 1;
1601da177e4SLinus Torvalds 		}
1611da177e4SLinus Torvalds 	}
1629769f4ebSJeremy White 	brelse(bh);
1631da177e4SLinus Torvalds 	return 0;
1641da177e4SLinus Torvalds }
1651da177e4SLinus Torvalds 
1661da177e4SLinus Torvalds struct dentry *isofs_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd)
1671da177e4SLinus Torvalds {
1681da177e4SLinus Torvalds 	int found;
169cd215237SBorislav Petkov 	unsigned long uninitialized_var(block);
170cd215237SBorislav Petkov 	unsigned long uninitialized_var(offset);
1714f819a78SArnd Bergmann 	struct isofs_sb_info *sbi = ISOFS_SB(dir->i_sb);
1721da177e4SLinus Torvalds 	struct inode *inode;
1731da177e4SLinus Torvalds 	struct page *page;
1741da177e4SLinus Torvalds 
1751da177e4SLinus Torvalds 	page = alloc_page(GFP_USER);
1761da177e4SLinus Torvalds 	if (!page)
1771da177e4SLinus Torvalds 		return ERR_PTR(-ENOMEM);
1781da177e4SLinus Torvalds 
1794f819a78SArnd Bergmann 	mutex_lock(&sbi->s_mutex);
1801da177e4SLinus Torvalds 	found = isofs_find_entry(dir, dentry,
1811da177e4SLinus Torvalds 				&block, &offset,
1821da177e4SLinus Torvalds 				page_address(page),
1831da177e4SLinus Torvalds 				1024 + page_address(page));
1841da177e4SLinus Torvalds 	__free_page(page);
1851da177e4SLinus Torvalds 
186*a9049376SAl Viro 	inode = found ? isofs_iget(dir->i_sb, block, offset) : NULL;
187*a9049376SAl Viro 
1884f819a78SArnd Bergmann 	mutex_unlock(&sbi->s_mutex);
189*a9049376SAl Viro 
1901da177e4SLinus Torvalds 	return d_splice_alias(inode, dentry);
1911da177e4SLinus Torvalds }
192