summaryrefslogtreecommitdiffstats
path: root/fs/isofs/dir.c
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1995-11-14 08:00:00 +0000
committer <ralf@linux-mips.org>1995-11-14 08:00:00 +0000
commite7c2a72e2680827d6a733931273a93461c0d8d1b (patch)
treec9abeda78ef7504062bb2e816bcf3e3c9d680112 /fs/isofs/dir.c
parentec6044459060a8c9ce7f64405c465d141898548c (diff)
Import of Linux/MIPS 1.3.0
Diffstat (limited to 'fs/isofs/dir.c')
-rw-r--r--fs/isofs/dir.c331
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;
+}