home *** CD-ROM | disk | FTP | other *** search
- /*
- * 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, char * compare, int dlen)
- {
- register int same __asm__("ax");
-
- if (!compare) return 0;
- /* "" means "." ---> so paths like "/usr/lib//libc.a" work */
- if (!len && (compare[0]==0) && (dlen==1))
- return 1;
-
- if (compare[0]==0 && dlen==1 && len == 1)
- compare = ".";
- if (compare[0]==1 && dlen==1 && len == 2) {
- 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;
- __asm__("cld\n\t"
- "repe ; cmpsb\n\t"
- "setz %%al"
- :"=a" (same)
- :"0" (0),"S" ((long) name),"D" ((long) compare),"c" (len)
- :"cx","di","si");
- return same;
- }
-
- /*
- * 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, int * ino, int * 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, rrflag, match;
- char * dpnt;
- 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 0;
- 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) {
- cpnt = kmalloc(1 << ISOFS_BLOCK_BITS, GFP_KERNEL);
- memcpy(cpnt, bh->b_data, bufsize);
- de = (struct iso_directory_record *)
- ((char *)cpnt + old_offset);
- brelse(bh);
- offset = f_pos & (bufsize - 1);
- block = isofs_bmap(dir,f_pos>>bufbits);
- if (!block || !(bh = bread(dir->i_dev,block,bufsize))) {
- kfree_s(cpnt, 1 << ISOFS_BLOCK_BITS);
- return 0;
- };
- memcpy((char *)cpnt+bufsize,bh->b_data,bufsize);
- }
-
- /* Handle the '.' case */
-
- if (de->name[0]==0 && de->name_len[0]==1) {
- inode_number = dir->i_ino;
- backlink = 0;
- }
-
- /* Handle the '..' case */
-
- if (de->name[0]==1 && de->name_len[0]==1) {
- #if 0
- printk("Doing .. (%d %d)",
- dir->i_sb->s_firstdatazone << bufbits,
- dir->i_ino);
- #endif
- if((dir->i_sb->u.isofs_sb.s_firstdatazone
- << bufbits) != dir->i_ino)
- inode_number = dir->u.isofs_i.i_backlink;
- else
- inode_number = dir->i_ino;
- backlink = 0;
- }
-
- dlen = de->name_len[0];
- dpnt = de->name;
- /* Now convert the filename in the buffer to lower case */
- rrflag = get_rock_ridge_filename(de, &dpnt, &dlen, dir);
- if (rrflag) {
- if (rrflag == -1) goto out; /* Relocated deep directory */
- } else {
- if(dir->i_sb->u.isofs_sb.s_mapping == 'n') {
- for (i = 0; i < dlen; i++) {
- c = dpnt[i];
- if (c >= 'A' && c <= 'Z') c |= 0x20; /* lower case */
- if (c == ';' && i == dlen-2 && dpnt[i+1] == '1') {
- dlen -= 2;
- break;
- }
- if (c == ';') c = '.';
- de->name[i] = c;
- }
- /* This allows us to match with and without a trailing
- period. */
- if(dpnt[dlen-1] == '.' && namelen == dlen-1)
- dlen--;
- }
- }
- match = isofs_match(namelen,name,dpnt,dlen);
- if (cpnt) {
- kfree_s(cpnt, 1 << ISOFS_BLOCK_BITS);
- cpnt = NULL;
- }
-
- if(rrflag) kfree(dpnt);
- 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.\n");
- goto out;
- }
- }
- *ino = inode_number;
- *ino_back = backlink;
- return bh;
- }
- }
- out:
- if (cpnt)
- kfree_s(cpnt, 1 << ISOFS_BLOCK_BITS);
- brelse(bh);
- return NULL;
- }
-
- int isofs_lookup(struct inode * dir,const char * name, int len,
- struct inode ** result)
- {
- int 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 (dir->i_dev == cache.dev &&
- dir->i_ino == cache.dir &&
- len == cache.dlen &&
- isofs_match(len, name, cache.filename, cache.dlen))
- {
- ino = cache.ino;
- ino_back = dir->i_ino;
- /* These two cases are special, but since they are at the start
- of the directory, we can just as easily search there */
- if (cache.dlen == 1 && cache.filename[0] == '.') ino = 0;
- if (cache.dlen == 2 && cache.filename[0] == '.' &&
- cache.filename[1] == '.') ino = 0;
- };
-
- if (!ino) {
- if (!(bh = isofs_find_entry(dir,name,len, &ino, &ino_back))) {
- iput(dir);
- return -ENOENT;
- }
- 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;
- }
-