diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1995-11-14 08:00:00 +0000 |
---|---|---|
committer | <ralf@linux-mips.org> | 1995-11-14 08:00:00 +0000 |
commit | e7c2a72e2680827d6a733931273a93461c0d8d1b (patch) | |
tree | c9abeda78ef7504062bb2e816bcf3e3c9d680112 /fs/isofs/dir.c | |
parent | ec6044459060a8c9ce7f64405c465d141898548c (diff) |
Import of Linux/MIPS 1.3.0
Diffstat (limited to 'fs/isofs/dir.c')
-rw-r--r-- | fs/isofs/dir.c | 331 |
1 files changed, 172 insertions, 159 deletions
diff --git a/fs/isofs/dir.c b/fs/isofs/dir.c index b1934db04..55fc5b9a8 100644 --- a/fs/isofs/dir.c +++ b/fs/isofs/dir.c @@ -8,6 +8,10 @@ * isofs directory handling functions */ +#ifdef MODULE +#include <linux/module.h> +#endif + #include <linux/errno.h> #include <asm/segment.h> @@ -22,12 +26,10 @@ #include <linux/sched.h> #include <linux/locks.h> -#define NAME_OFFSET(de) ((int) ((de)->d_name - (char *) (de))) -#define ROUND_UP(x) (((x)+3) & ~3) +static int isofs_readdir(struct inode *, struct file *, void *, filldir_t); -static int isofs_readdir(struct inode *, struct file *, struct dirent *, int); - -static struct file_operations isofs_dir_operations = { +static struct file_operations isofs_dir_operations = +{ NULL, /* lseek - default */ NULL, /* read */ NULL, /* write - bad */ @@ -42,14 +44,15 @@ static struct file_operations isofs_dir_operations = { /* * directories can handle most operations... */ -struct inode_operations isofs_dir_inode_operations = { +struct inode_operations isofs_dir_inode_operations = +{ &isofs_dir_operations, /* default directory file-ops */ - NULL, /* create */ + NULL, /* create */ isofs_lookup, /* lookup */ - NULL, /* link */ - NULL, /* unlink */ - NULL, /* symlink */ - NULL, /* mkdir */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ NULL, /* rmdir */ NULL, /* mknod */ NULL, /* rename */ @@ -60,199 +63,209 @@ struct inode_operations isofs_dir_inode_operations = { NULL /* permission */ }; -static int isofs_readdir(struct inode * inode, struct file * filp, - struct dirent * dirent, int count) +static int parent_inode_number(struct inode * inode, struct iso_directory_record * de) +{ + int inode_number = inode->i_ino; + + if ((inode->i_sb->u.isofs_sb.s_firstdatazone) != inode->i_ino) + inode_number = inode->u.isofs_i.i_backlink; + + if (inode_number != -1) + return inode_number; + + /* This should never happen, but who knows. Try to be forgiving */ + return isofs_lookup_grandparent(inode, find_rock_ridge_relocation(de, inode)); +} + +static int isofs_name_translate(char * old, int len, char * new) +{ + int i, c; + + for (i = 0; i < len; i++) { + c = old[i]; + if (!c) + break; + if (c >= 'A' && c <= 'Z') + c |= 0x20; /* lower case */ + + /* Drop trailing '.;1' (ISO9660:1988 7.5.1 requires period) */ + if (c == '.' && i == len - 3 && old[i + 1] == ';' && old[i + 2] == '1') + break; + + /* Drop trailing ';1' */ + if (c == ';' && i == len - 2 && old[i + 1] == '1') + break; + + /* Convert remaining ';' to '.' */ + if (c == ';') + c = '.'; + + new[i] = c; + } + return i; +} + +/* + * This should _really_ be cleaned up some day.. + */ +static int do_isofs_readdir(struct inode *inode, struct file *filp, + void *dirent, filldir_t filldir, + char * tmpname, struct iso_directory_record * tmpde) { unsigned long bufsize = ISOFS_BUFFER_SIZE(inode); unsigned char bufbits = ISOFS_BUFFER_BITS(inode); - unsigned int block,offset,i, j; - char c = 0; + unsigned int block, offset; int inode_number; - struct buffer_head * bh; - void * cpnt = NULL; - unsigned int old_offset; - int dlen, rrflag; + struct buffer_head *bh; + int len, rrflag; int high_sierra = 0; - char * dpnt, *dpnt1; - struct iso_directory_record * de; - - dpnt1 = NULL; - if (!inode || !S_ISDIR(inode->i_mode)) - return -EBADF; - + char *name; + struct iso_directory_record *de; + offset = filp->f_pos & (bufsize - 1); - block = isofs_bmap(inode,filp->f_pos>>bufbits); + block = isofs_bmap(inode, filp->f_pos >> bufbits); - if(!block) return 0; + if (!block) + return 0; - if(!(bh = breada(inode->i_dev, block, bufsize, filp->f_pos, inode->i_size))) - return 0; + if (!(bh = breada(inode->i_dev, block, bufsize, filp->f_pos, inode->i_size))) + return 0; while (filp->f_pos < inode->i_size) { + int de_len, next_offset; #ifdef DEBUG printk("Block, offset, f_pos: %x %x %x\n", block, offset, filp->f_pos); #endif de = (struct iso_directory_record *) (bh->b_data + offset); inode_number = (block << bufbits) + (offset & (bufsize - 1)); - + + de_len = *(unsigned char *) de; + /* If the length byte is zero, we should move on to the next CDROM sector. If we are at the end of the directory, we kick out of the while loop. */ - - if (*((unsigned char *) de) == 0) { + + if (de_len == 0) { brelse(bh); - offset = 0; filp->f_pos = ((filp->f_pos & ~(ISOFS_BLOCK_SIZE - 1)) + ISOFS_BLOCK_SIZE); - block = isofs_bmap(inode,(filp->f_pos)>>bufbits); - if (!block - || !(bh = breada(inode->i_dev, block, bufsize, filp->f_pos, - inode->i_size))) + offset = 0; + block = isofs_bmap(inode, (filp->f_pos) >> bufbits); + if (!block) + return 0; + bh = breada(inode->i_dev, block, bufsize, filp->f_pos, inode->i_size); + if (!bh) return 0; continue; } /* Make sure that the entire directory record is in the current bh block. - If not, we malloc a buffer, and put the two halves together, - so that we can cleanly read the block */ - - old_offset = offset; - offset += *((unsigned char *) de); - filp->f_pos += *((unsigned char *) de); - - if (offset > bufsize) { - unsigned int frag1; - frag1 = bufsize - old_offset; - cpnt = kmalloc(*((unsigned char *) de),GFP_KERNEL); - if (!cpnt) return 0; - memcpy(cpnt, bh->b_data + old_offset, frag1); - de = (struct iso_directory_record *) ((char *)cpnt); + If not, put the two halves together in "tmpde" */ + next_offset = offset + de_len; + if (next_offset > bufsize) { + next_offset &= (bufsize - 1); + memcpy(tmpde, de, bufsize - offset); brelse(bh); - offset = filp->f_pos & (bufsize - 1); - block = isofs_bmap(inode,(filp->f_pos)>> bufbits); - if (!block - || !(bh = breada(inode->i_dev, block, bufsize, - filp->f_pos, inode->i_size))) { - kfree(cpnt); + block = isofs_bmap(inode, (filp->f_pos + de_len) >> bufbits); + if (!block) return 0; - }; - memcpy((char *)cpnt+frag1, bh->b_data, offset); + bh = breada(inode->i_dev, block, bufsize, filp->f_pos+de_len, inode->i_size); + if (!bh) + return 0; + memcpy(bufsize - offset + (char *) tmpde, bh->b_data, next_offset); + de = tmpde; } - - /* Handle the case of the '.' directory */ + offset = next_offset; - rrflag = 0; - i = 1; + /* Handle the case of the '.' directory */ if (de->name_len[0] == 1 && de->name[0] == 0) { - put_fs_byte('.',dirent->d_name); - inode_number = inode->i_ino; - dpnt = "."; + if (filldir(dirent, ".", 1, filp->f_pos, inode->i_ino) < 0) + break; + filp->f_pos += de_len; + continue; } - + /* Handle the case of the '..' directory */ - - else if (de->name_len[0] == 1 && de->name[0] == 1) { - put_fs_byte('.',dirent->d_name); - put_fs_byte('.',dirent->d_name+1); - i = 2; - dpnt = ".."; - if((inode->i_sb->u.isofs_sb.s_firstdatazone) != inode->i_ino) - inode_number = inode->u.isofs_i.i_backlink; - else - inode_number = inode->i_ino; - - /* This should never happen, but who knows. Try to be forgiving */ - if(inode_number == -1) { - inode_number = - isofs_lookup_grandparent(inode, - find_rock_ridge_relocation(de, inode)); - if(inode_number == -1){ /* Should never happen */ - printk("Backlink not properly set.\n"); - goto out; - }; - } + if (de->name_len[0] == 1 && de->name[0] == 1) { + inode_number = parent_inode_number(inode, de); + if (inode_number == -1) + break; + if (filldir(dirent, "..", 2, filp->f_pos, inode_number) < 0) + break; + filp->f_pos += de_len; + continue; } - + /* Handle everything else. Do name translation if there is no Rock Ridge NM field. */ - - else { - /* Do not report hidden or associated files */ - high_sierra = inode->i_sb->u.isofs_sb.s_high_sierra; - if (de->flags[-high_sierra] & 5) { - if (cpnt) { - kfree(cpnt); - cpnt = NULL; - }; - continue; - } - dlen = de->name_len[0]; - dpnt = de->name; - i = dlen; - rrflag = get_rock_ridge_filename(de, &dpnt, &dlen, inode); - if (rrflag) { - if (rrflag == -1) { /* This is a rock ridge reloc dir */ - if (cpnt) { - kfree(cpnt); - cpnt = NULL; - }; - continue; - }; - i = dlen; + + if (inode->i_sb->u.isofs_sb.s_unhide == 'n') { + /* Do not report hidden or associated files */ + high_sierra = inode->i_sb->u.isofs_sb.s_high_sierra; + if (de->flags[-high_sierra] & 5) { + filp->f_pos += de_len; + continue; } - else - if(inode->i_sb->u.isofs_sb.s_mapping == 'n') { - dpnt1 = dpnt; - dpnt = kmalloc(dlen, GFP_KERNEL); - if (!dpnt) goto out; - for (i = 0; i < dlen && i < NAME_MAX; i++) { - if (!(c = dpnt1[i])) break; - if (c >= 'A' && c <= 'Z') c |= 0x20; /* lower case */ - if (c == '.' && i == dlen-3 && de->name[i+1] == ';' && de->name[i+2] == '1') - break; /* Drop trailing '.;1' (ISO9660:1988 7.5.1 requires period) */ - if (c == ';' && i == dlen-2 && de->name[i+1] == '1') - break; /* Drop trailing ';1' */ - if (c == ';') c = '.'; /* Convert remaining ';' to '.' */ - dpnt[i] = c; - } - } - for(j=0; j<i; j++) - put_fs_byte(dpnt[j],j+dirent->d_name); /* And save it */ - if(dpnt1) { - kfree(dpnt); - dpnt = dpnt1; + } + + /* Check Rock Ridge name translation.. */ + len = de->name_len[0]; + name = de->name; + rrflag = get_rock_ridge_filename(de, &name, &len, inode); + if (rrflag) { + /* rrflag == 1 means that we have a new name (kmalloced) */ + if (rrflag == 1) { + rrflag = filldir(dirent, name, len, filp->f_pos, inode_number); + kfree(name); /* this was allocated in get_r_r_filename.. */ + if (rrflag < 0) + break; } - - dcache_add(inode, dpnt, i, inode_number); - }; -#if 0 - printk("Nchar: %d\n",i); -#endif + filp->f_pos += de_len; + continue; + } - if (rrflag) kfree(dpnt); - if (cpnt) { - kfree(cpnt); - cpnt = NULL; - }; - - if (i) { - put_fs_long(inode_number, &dirent->d_ino); - put_fs_byte(0,i+dirent->d_name); - put_fs_word(i,&dirent->d_reclen); - brelse(bh); - return ROUND_UP(NAME_OFFSET(dirent) + i + 1); + if (inode->i_sb->u.isofs_sb.s_mapping == 'n') { + len = isofs_name_translate(name, len, tmpname); + if (filldir(dirent, tmpname, len, filp->f_pos, inode_number) < 0) + break; + filp->f_pos += de_len; + continue; } - } - /* We go here for any condition we cannot handle. We also drop through - to here at the end of the directory. */ - out: - if (cpnt) - kfree(cpnt); + + if (filldir(dirent, name, len, filp->f_pos, inode_number) < 0) + break; + + filp->f_pos += de_len; + continue; + } brelse(bh); return 0; } +/* + * Handle allocation of temporary space for name translation and + * handling split directory entries.. The real work is done by + * "do_isofs_readdir()". + */ +static int isofs_readdir(struct inode *inode, struct file *filp, + void *dirent, filldir_t filldir) +{ + int result; + char * tmpname; + struct iso_directory_record * tmpde; + + if (!inode || !S_ISDIR(inode->i_mode)) + return -EBADF; + + tmpname = (char *) __get_free_page(GFP_KERNEL); + if (!tmpname) + return -ENOMEM; + tmpde = (struct iso_directory_record *) (tmpname+256); + result = do_isofs_readdir(inode, filp, dirent, filldir, tmpname, tmpde); + free_page((unsigned long) tmpname); + return result; +} |