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; }