/* * linux/fs/isofs/dir.c * * (C) 1992, 1993, 1994 Eric Youngdale Modified for ISO9660 filesystem. * * (C) 1991 Linus Torvalds - minix filesystem * * isofs directory handling functions */ #ifdef MODULE #include #endif #include #include #include #include #include #include #include #include #include #include #include #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 *, struct dirent *, int); static struct file_operations isofs_dir_operations = { NULL, /* lseek - default */ NULL, /* read */ NULL, /* write - bad */ isofs_readdir, /* readdir */ NULL, /* select - default */ NULL, /* ioctl - default */ NULL, /* no special open code */ NULL, /* no special release code */ NULL /* fsync */ }; /* * directories can handle most operations... */ struct inode_operations isofs_dir_inode_operations = { &isofs_dir_operations, /* default directory file-ops */ NULL, /* create */ isofs_lookup, /* lookup */ NULL, /* link */ NULL, /* unlink */ NULL, /* symlink */ NULL, /* mkdir */ NULL, /* rmdir */ NULL, /* mknod */ NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ isofs_bmap, /* bmap */ NULL, /* truncate */ NULL /* permission */ }; static int isofs_readdir(struct inode * inode, struct file * filp, struct dirent * dirent, int count) { unsigned long bufsize = ISOFS_BUFFER_SIZE(inode); unsigned char bufbits = ISOFS_BUFFER_BITS(inode); unsigned int block,offset,i, j; char c = 0; int inode_number; struct buffer_head * bh; void * cpnt = NULL; unsigned int old_offset; int dlen, rrflag; int high_sierra = 0; char * dpnt, *dpnt1; struct iso_directory_record * de; dpnt1 = NULL; if (!inode || !S_ISDIR(inode->i_mode)) return -EBADF; offset = filp->f_pos & (bufsize - 1); block = isofs_bmap(inode,filp->f_pos>>bufbits); if(!block) 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) { #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)); /* 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) { 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))) 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); 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); return 0; }; memcpy((char *)cpnt+frag1, bh->b_data, offset); } /* Handle the case of the '.' directory */ rrflag = 0; i = 1; if (de->name_len[0] == 1 && de->name[0] == 0) { put_fs_byte('.',dirent->d_name); inode_number = inode->i_ino; dpnt = "."; } /* 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; }; } } /* Handle everything else. Do name translation if there is no Rock Ridge NM field. */ else { 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) { 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; } 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; jd_name); /* And save it */ if(dpnt1) { kfree(dpnt); dpnt = dpnt1; } dcache_add(inode, dpnt, i, inode_number); }; #if 0 printk("Nchar: %d\n",i); #endif 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); } } /* 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); brelse(bh); return 0; }