xref: /openbmc/linux/fs/isofs/namei.c (revision 94f2f715771d0aa5554451d1e2a920f11b8be3fe)
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 
91da177e4SLinus Torvalds #include <linux/config.h>	/* Joliet? */
101da177e4SLinus Torvalds #include <linux/smp_lock.h>
11*94f2f715SAl 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
191da177e4SLinus Torvalds isofs_cmp(struct dentry * dentry, const char * compare, int dlen)
201da177e4SLinus Torvalds {
211da177e4SLinus Torvalds 	struct qstr qstr;
221da177e4SLinus Torvalds 
231da177e4SLinus Torvalds 	if (!compare)
241da177e4SLinus Torvalds 		return 1;
251da177e4SLinus Torvalds 
261da177e4SLinus Torvalds 	/* check special "." and ".." files */
271da177e4SLinus Torvalds 	if (dlen == 1) {
281da177e4SLinus Torvalds 		/* "." */
291da177e4SLinus Torvalds 		if (compare[0] == 0) {
301da177e4SLinus Torvalds 			if (!dentry->d_name.len)
311da177e4SLinus Torvalds 				return 0;
321da177e4SLinus Torvalds 			compare = ".";
331da177e4SLinus Torvalds 		} else if (compare[0] == 1) {
341da177e4SLinus Torvalds 			compare = "..";
351da177e4SLinus Torvalds 			dlen = 2;
361da177e4SLinus Torvalds 		}
371da177e4SLinus Torvalds 	}
381da177e4SLinus Torvalds 
391da177e4SLinus Torvalds 	qstr.name = compare;
401da177e4SLinus Torvalds 	qstr.len = dlen;
411da177e4SLinus Torvalds 	return dentry->d_op->d_compare(dentry, &dentry->d_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;
1151da177e4SLinus Torvalds 
1161da177e4SLinus Torvalds 		if (sbi->s_rock &&
1171da177e4SLinus Torvalds 		    ((i = get_rock_ridge_filename(de, tmpname, dir)))) {
1181da177e4SLinus Torvalds 			dlen = i; 	/* possibly -1 */
1191da177e4SLinus Torvalds 			dpnt = tmpname;
1201da177e4SLinus Torvalds #ifdef CONFIG_JOLIET
1211da177e4SLinus Torvalds 		} else if (sbi->s_joliet_level) {
1221da177e4SLinus Torvalds 			dlen = get_joliet_filename(de, tmpname, dir);
1231da177e4SLinus Torvalds 			dpnt = tmpname;
1241da177e4SLinus Torvalds #endif
1251da177e4SLinus Torvalds 		} else if (sbi->s_mapping == 'a') {
1261da177e4SLinus Torvalds 			dlen = get_acorn_filename(de, tmpname, dir);
1271da177e4SLinus Torvalds 			dpnt = tmpname;
1281da177e4SLinus Torvalds 		} else if (sbi->s_mapping == 'n') {
1291da177e4SLinus Torvalds 			dlen = isofs_name_translate(de, tmpname, dir);
1301da177e4SLinus Torvalds 			dpnt = tmpname;
1311da177e4SLinus Torvalds 		}
1321da177e4SLinus Torvalds 
1331da177e4SLinus Torvalds 		/*
1341da177e4SLinus Torvalds 		 * Skip hidden or associated files unless unhide is set
1351da177e4SLinus Torvalds 		 */
1361da177e4SLinus Torvalds 		match = 0;
1371da177e4SLinus Torvalds 		if (dlen > 0 &&
1381da177e4SLinus Torvalds 		    (!(de->flags[-sbi->s_high_sierra] & 5)
1391da177e4SLinus Torvalds 		     || sbi->s_unhide == 'y'))
1401da177e4SLinus Torvalds 		{
1411da177e4SLinus Torvalds 			match = (isofs_cmp(dentry,dpnt,dlen) == 0);
1421da177e4SLinus Torvalds 		}
1431da177e4SLinus Torvalds 		if (match) {
1441da177e4SLinus Torvalds 			isofs_normalize_block_and_offset(de,
1451da177e4SLinus Torvalds 							 &block_saved,
1461da177e4SLinus Torvalds 							 &offset_saved);
1471da177e4SLinus Torvalds                         *block_rv = block_saved;
1481da177e4SLinus Torvalds                         *offset_rv = offset_saved;
1491da177e4SLinus Torvalds 			if (bh) brelse(bh);
1501da177e4SLinus Torvalds 			return 1;
1511da177e4SLinus Torvalds 		}
1521da177e4SLinus Torvalds 	}
1531da177e4SLinus Torvalds 	if (bh) brelse(bh);
1541da177e4SLinus Torvalds 	return 0;
1551da177e4SLinus Torvalds }
1561da177e4SLinus Torvalds 
1571da177e4SLinus Torvalds struct dentry *isofs_lookup(struct inode * dir, struct dentry * dentry, struct nameidata *nd)
1581da177e4SLinus Torvalds {
1591da177e4SLinus Torvalds 	int found;
1601da177e4SLinus Torvalds 	unsigned long block, offset;
1611da177e4SLinus Torvalds 	struct inode *inode;
1621da177e4SLinus Torvalds 	struct page *page;
1631da177e4SLinus Torvalds 
1641da177e4SLinus Torvalds 	dentry->d_op = dir->i_sb->s_root->d_op;
1651da177e4SLinus Torvalds 
1661da177e4SLinus Torvalds 	page = alloc_page(GFP_USER);
1671da177e4SLinus Torvalds 	if (!page)
1681da177e4SLinus Torvalds 		return ERR_PTR(-ENOMEM);
1691da177e4SLinus Torvalds 
1701da177e4SLinus Torvalds 	lock_kernel();
1711da177e4SLinus Torvalds 	found = isofs_find_entry(dir, dentry,
1721da177e4SLinus Torvalds 				 &block, &offset,
1731da177e4SLinus Torvalds 				 page_address(page),
1741da177e4SLinus Torvalds 				 1024 + page_address(page));
1751da177e4SLinus Torvalds 	__free_page(page);
1761da177e4SLinus Torvalds 
1771da177e4SLinus Torvalds 	inode = NULL;
1781da177e4SLinus Torvalds 	if (found) {
1791da177e4SLinus Torvalds 		inode = isofs_iget(dir->i_sb, block, offset);
1801da177e4SLinus Torvalds 		if (!inode) {
1811da177e4SLinus Torvalds 			unlock_kernel();
1821da177e4SLinus Torvalds 			return ERR_PTR(-EACCES);
1831da177e4SLinus Torvalds 		}
1841da177e4SLinus Torvalds 	}
1851da177e4SLinus Torvalds 	unlock_kernel();
1861da177e4SLinus Torvalds 	if (inode)
1871da177e4SLinus Torvalds 		return d_splice_alias(inode, dentry);
1881da177e4SLinus Torvalds 	d_add(dentry, inode);
1891da177e4SLinus Torvalds 	return NULL;
1901da177e4SLinus Torvalds }
191