OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [trunk/] [rc203soc/] [sw/] [uClinux/] [fs/] [isofs/] [namei.c] - Rev 1765

Compare with Previous | Blame | View Log

/*
 *  linux/fs/isofs/namei.c
 *
 *  (C) 1992  Eric Youngdale Modified for ISO9660 filesystem.
 *
 *  (C) 1991  Linus Torvalds - minix filesystem
 */
 
#include <linux/sched.h>
#include <linux/iso_fs.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/stat.h>
#include <linux/fcntl.h>
#include <asm/segment.h>
#include <linux/malloc.h>
 
#include <linux/errno.h>
 
/*
 * ok, we cannot use strncmp, as the name is not in our data space.
 * Thus we'll have to use isofs_match. No big problem. Match also makes
 * some sanity tests.
 *
 * NOTE! unlike strncmp, isofs_match returns 1 for success, 0 for failure.
 */
static int isofs_match(int len,const char * name, const char * compare, int dlen)
{
	if (!compare)
		return 0;
 
	/* check special "." and ".." files */
	if (dlen == 1) {
		/* "." */
		if (compare[0] == 0) {
			if (!len)
				return 1;
			compare = ".";
		} else if (compare[0] == 1) {
			compare = "..";
			dlen = 2;
		}
	}
#if 0
	if (len <= 2) printk("Match: %d %d %s %d %d \n",len,dlen,compare,de->name[0], dlen);
#endif
 
	if (dlen != len)
		return 0;
	return !memcmp(name, compare, len);
}
 
/*
 *	isofs_find_entry()
 *
 * finds an entry in the specified directory with the wanted name. It
 * returns the cache buffer in which the entry was found, and the entry
 * itself (as an inode number). It does NOT read the inode of the
 * entry - you'll have to do that yourself if you want to.
 */
static struct buffer_head *
isofs_find_entry(struct inode * dir, const char * name, int namelen,
		 unsigned long * ino, unsigned long * ino_back)
{
	unsigned long bufsize = ISOFS_BUFFER_SIZE(dir);
	unsigned char bufbits = ISOFS_BUFFER_BITS(dir);
	unsigned int block, i, f_pos, offset, inode_number;
	struct buffer_head * bh;
	void * cpnt = NULL;
	unsigned int old_offset;
	unsigned int backlink;
	int dlen, match;
	char * dpnt;
	unsigned char *page = NULL;
	struct iso_directory_record * de;
	char c;
 
	*ino = 0;
	if (!dir) return NULL;
 
	if (!(block = dir->u.isofs_i.i_first_extent)) return NULL;
 
	f_pos = 0;
 
	offset = f_pos & (bufsize - 1);
	block = isofs_bmap(dir,f_pos >> bufbits);
 
	if (!block || !(bh = bread(dir->i_dev,block,bufsize))) return NULL;
 
	while (f_pos < dir->i_size) {
		de = (struct iso_directory_record *) (bh->b_data + offset);
		backlink = dir->i_ino;
		inode_number = (block << bufbits) + (offset & (bufsize - 1));
 
		/* If byte is zero, this is the end of file, or time to move to
		   the next sector. Usually 2048 byte boundaries. */
 
		if (*((unsigned char *) de) == 0) {
			brelse(bh);
			offset = 0;
			f_pos = ((f_pos & ~(ISOFS_BLOCK_SIZE - 1))
				 + ISOFS_BLOCK_SIZE);
			block = isofs_bmap(dir,f_pos>>bufbits);
			if (!block || !(bh = bread(dir->i_dev,block,bufsize)))
				return NULL;
			continue; /* Will kick out if past end of directory */
		}
 
		old_offset = offset;
		offset += *((unsigned char *) de);
		f_pos += *((unsigned char *) de);
 
		/* Handle case where the directory entry spans two blocks.
		   Usually 1024 byte boundaries */
		if (offset >= bufsize) {
		        unsigned int frag1;
			frag1 = bufsize - old_offset;
			cpnt = kmalloc(*((unsigned char *) de),GFP_KERNEL);
			if (!cpnt) return NULL;
			memcpy(cpnt, bh->b_data + old_offset, frag1);
 
			de = (struct iso_directory_record *) cpnt;
			brelse(bh);
			offset = f_pos & (bufsize - 1);
			block = isofs_bmap(dir,f_pos>>bufbits);
			if (!block || !(bh = bread(dir->i_dev,block,bufsize))) {
			        kfree(cpnt);
				return NULL;
			};
			memcpy((char *)cpnt+frag1, bh->b_data, offset);
		}
 
		dlen = de->name_len[0];
		dpnt = de->name;
 
		/* Handle the '.' case */
 
		if (*dpnt==0 && dlen==1) {
			inode_number = dir->i_ino;
			backlink = 0;
		}
 
		/* Handle the '..' case */
 
		else if (*dpnt==1 && dlen==1) {
#if 0
			printk("Doing .. (%d %d)",
			       dir->i_sb->s_firstdatazone,
			       dir->i_ino);
#endif
			if((dir->i_sb->u.isofs_sb.s_firstdatazone) != dir->i_ino)
 				inode_number = dir->u.isofs_i.i_backlink;
			else
				inode_number = dir->i_ino;
			backlink = 0;
		} else {
			if (dir->i_sb->u.isofs_sb.s_rock ||
			    dir->i_sb->u.isofs_sb.s_joliet_level) {
				page = (unsigned char *)
					__get_free_page(GFP_KERNEL);
				if (!page) return NULL;
			}
			if (dir->i_sb->u.isofs_sb.s_rock &&
			    ((i = get_rock_ridge_filename(de, page, dir)))){
				if (i == -1)
					goto out;/* Relocated deep directory */
				dlen = i;
				dpnt = page;
			} else if (dir->i_sb->u.isofs_sb.s_joliet_level) {
				dlen = get_joliet_filename(de, dir, page);
				dpnt = page;
			} else if (dir->i_sb->u.isofs_sb.s_mapping == 'n') {
				for (i = 0; i < dlen; i++) {
					c = dpnt[i];
					/* lower case */
					if (c >= 'A' && c <= 'Z') c |= 0x20;
					if (c == ';' && i == dlen-2 && dpnt[i+1] == '1') {
						dlen -= 2;
						break;
					}
					if (c == ';') c = '.';
					dpnt[i] = c;
				}
				/* This allows us to match with and without
				 * a trailing period. */
				if(dpnt[dlen-1] == '.' && namelen == dlen-1)
					dlen--;
			}
		}
		/*
		 * Skip hidden or associated files unless unhide is set 
		 */
		match = 0;
		if(   !(de->flags[-dir->i_sb->u.isofs_sb.s_high_sierra] & 5)
		   || dir->i_sb->u.isofs_sb.s_unhide == 'y' )
		{
			match = isofs_match(namelen,name,dpnt,dlen);
		}
 
		if (cpnt)
		{
			kfree(cpnt);
			cpnt = NULL;
		}
 
		if (page) free_page((unsigned long) page);
		if (match) {
			if(inode_number == -1) {
				/* Should only happen for the '..' entry */
				inode_number = 
					isofs_lookup_grandparent(dir,
					   find_rock_ridge_relocation(de,dir));
				if(inode_number == -1){
					/* Should never happen */
					printk("Backlink not properly set %x %lx.\n",
					       isonum_733(de->extent),
					       dir->i_ino);
					goto out;
				}
			}
			*ino = inode_number;
			*ino_back = backlink;
			return bh;
		}
	}
 out:
	if (cpnt)
		kfree(cpnt);
	brelse(bh);
	return NULL;
}
 
int isofs_lookup(struct inode * dir,const char * name, int len,
	struct inode ** result)
{
	unsigned long ino, ino_back;
	struct buffer_head * bh;
 
#ifdef DEBUG
	printk("lookup: %x %d\n",dir->i_ino, len);
#endif
	*result = NULL;
	if (!dir)
		return -ENOENT;
 
	if (!S_ISDIR(dir->i_mode)) {
		iput(dir);
		return -ENOENT;
	}
 
	ino = 0;
 
	if (dcache_lookup(dir, name, len, &ino)) ino_back = dir->i_ino;
 
 
	if (!ino) {
		char *lcname;
 
		/* First try the original name. If that doesn't work and the fs
		 * was mounted with check=relaxed, convert the name to lower
		 * case and try again.
		 */
		if (!(bh = isofs_find_entry(dir,name,len, &ino, &ino_back))
		    && dir->i_sb->u.isofs_sb.s_name_check == 'r'
		    && (lcname = kmalloc(len, GFP_KERNEL)) != NULL) {
			int i;
			char c;
 
			for (i=0; i<len; i++) {
				c = name[i];
				if (c >= 'A' && c <= 'Z') c |= 0x20;
				lcname[i] = c;
			}
			bh = isofs_find_entry(dir,lcname,len, &ino, &ino_back);
			kfree(lcname);
		}
 
		if (!bh) {
			iput(dir);
	  		return -ENOENT;
		}
		if (ino_back == dir->i_ino) {
			dcache_add(dir, name, len, ino);
		}
		brelse(bh);
	}
 
	if (!(*result = iget(dir->i_sb,ino))) {
		iput(dir);
		return -EACCES;
	}
 
	/* We need this backlink for the ".." entry unless the name that we
	   are looking up traversed a mount point (in which case the inode
	   may not even be on an iso9660 filesystem, and writing to
	   u.isofs_i would only cause memory corruption).
	*/
 
	if (ino_back && !(*result)->i_pipe && (*result)->i_sb == dir->i_sb) {
		(*result)->u.isofs_i.i_backlink = ino_back; 
	}
 
	iput(dir);
	return 0;
}
 

Compare with Previous | Blame | View Log

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.