linux/fs/isofs/dir.c
<<
>>
Prefs
   1/*
   2 *  linux/fs/isofs/dir.c
   3 *
   4 *  (C) 1992, 1993, 1994  Eric Youngdale Modified for ISO 9660 filesystem.
   5 *
   6 *  (C) 1991  Linus Torvalds - minix filesystem
   7 *
   8 *  Steve Beynon                       : Missing last directory entries fixed
   9 *  (stephen@askone.demon.co.uk)      : 21st June 1996
  10 *
  11 *  isofs directory handling functions
  12 */
  13#include <linux/smp_lock.h>
  14#include "isofs.h"
  15
  16int isofs_name_translate(struct iso_directory_record *de, char *new, struct inode *inode)
  17{
  18        char * old = de->name;
  19        int len = de->name_len[0];
  20        int i;
  21
  22        for (i = 0; i < len; i++) {
  23                unsigned char c = old[i];
  24                if (!c)
  25                        break;
  26
  27                if (c >= 'A' && c <= 'Z')
  28                        c |= 0x20;      /* lower case */
  29
  30                /* Drop trailing '.;1' (ISO 9660:1988 7.5.1 requires period) */
  31                if (c == '.' && i == len - 3 && old[i + 1] == ';' && old[i + 2] == '1')
  32                        break;
  33
  34                /* Drop trailing ';1' */
  35                if (c == ';' && i == len - 2 && old[i + 1] == '1')
  36                        break;
  37
  38                /* Convert remaining ';' to '.' */
  39                /* Also '/' to '.' (broken Acorn-generated ISO9660 images) */
  40                if (c == ';' || c == '/')
  41                        c = '.';
  42
  43                new[i] = c;
  44        }
  45        return i;
  46}
  47
  48/* Acorn extensions written by Matthew Wilcox <willy@bofh.ai> 1998 */
  49int get_acorn_filename(struct iso_directory_record *de,
  50                            char *retname, struct inode *inode)
  51{
  52        int std;
  53        unsigned char *chr;
  54        int retnamlen = isofs_name_translate(de, retname, inode);
  55
  56        if (retnamlen == 0)
  57                return 0;
  58        std = sizeof(struct iso_directory_record) + de->name_len[0];
  59        if (std & 1)
  60                std++;
  61        if ((*((unsigned char *) de) - std) != 32)
  62                return retnamlen;
  63        chr = ((unsigned char *) de) + std;
  64        if (strncmp(chr, "ARCHIMEDES", 10))
  65                return retnamlen;
  66        if ((*retname == '_') && ((chr[19] & 1) == 1))
  67                *retname = '!';
  68        if (((de->flags[0] & 2) == 0) && (chr[13] == 0xff)
  69                && ((chr[12] & 0xf0) == 0xf0)) {
  70                retname[retnamlen] = ',';
  71                sprintf(retname+retnamlen+1, "%3.3x",
  72                        ((chr[12] & 0xf) << 8) | chr[11]);
  73                retnamlen += 4;
  74        }
  75        return retnamlen;
  76}
  77
  78/*
  79 * This should _really_ be cleaned up some day..
  80 */
  81static int do_isofs_readdir(struct inode *inode, struct file *filp,
  82                void *dirent, filldir_t filldir,
  83                char *tmpname, struct iso_directory_record *tmpde)
  84{
  85        unsigned long bufsize = ISOFS_BUFFER_SIZE(inode);
  86        unsigned char bufbits = ISOFS_BUFFER_BITS(inode);
  87        unsigned long block, offset, block_saved, offset_saved;
  88        unsigned long inode_number = 0; /* Quiet GCC */
  89        struct buffer_head *bh = NULL;
  90        int len;
  91        int map;
  92        int first_de = 1;
  93        char *p = NULL;         /* Quiet GCC */
  94        struct iso_directory_record *de;
  95        struct isofs_sb_info *sbi = ISOFS_SB(inode->i_sb);
  96
  97        offset = filp->f_pos & (bufsize - 1);
  98        block = filp->f_pos >> bufbits;
  99
 100        while (filp->f_pos < inode->i_size) {
 101                int de_len;
 102
 103                if (!bh) {
 104                        bh = isofs_bread(inode, block);
 105                        if (!bh)
 106                                return 0;
 107                }
 108
 109                de = (struct iso_directory_record *) (bh->b_data + offset);
 110
 111                de_len = *(unsigned char *) de;
 112
 113                /*
 114                 * If the length byte is zero, we should move on to the next
 115                 * CDROM sector.  If we are at the end of the directory, we
 116                 * kick out of the while loop.
 117                 */
 118
 119                if (de_len == 0) {
 120                        brelse(bh);
 121                        bh = NULL;
 122                        filp->f_pos = (filp->f_pos + ISOFS_BLOCK_SIZE) & ~(ISOFS_BLOCK_SIZE - 1);
 123                        block = filp->f_pos >> bufbits;
 124                        offset = 0;
 125                        continue;
 126                }
 127
 128                block_saved = block;
 129                offset_saved = offset;
 130                offset += de_len;
 131
 132                /* Make sure we have a full directory entry */
 133                if (offset >= bufsize) {
 134                        int slop = bufsize - offset + de_len;
 135                        memcpy(tmpde, de, slop);
 136                        offset &= bufsize - 1;
 137                        block++;
 138                        brelse(bh);
 139                        bh = NULL;
 140                        if (offset) {
 141                                bh = isofs_bread(inode, block);
 142                                if (!bh)
 143                                        return 0;
 144                                memcpy((void *) tmpde + slop, bh->b_data, offset);
 145                        }
 146                        de = tmpde;
 147                }
 148                /* Basic sanity check, whether name doesn't exceed dir entry */
 149                if (de_len < de->name_len[0] +
 150                                        sizeof(struct iso_directory_record)) {
 151                        printk(KERN_NOTICE "iso9660: Corrupted directory entry"
 152                               " in block %lu of inode %lu\n", block,
 153                               inode->i_ino);
 154                        return -EIO;
 155                }
 156
 157                if (first_de) {
 158                        isofs_normalize_block_and_offset(de,
 159                                                        &block_saved,
 160                                                        &offset_saved);
 161                        inode_number = isofs_get_ino(block_saved,
 162                                                        offset_saved, bufbits);
 163                }
 164
 165                if (de->flags[-sbi->s_high_sierra] & 0x80) {
 166                        first_de = 0;
 167                        filp->f_pos += de_len;
 168                        continue;
 169                }
 170                first_de = 1;
 171
 172                /* Handle the case of the '.' directory */
 173                if (de->name_len[0] == 1 && de->name[0] == 0) {
 174                        if (filldir(dirent, ".", 1, filp->f_pos, inode->i_ino, DT_DIR) < 0)
 175                                break;
 176                        filp->f_pos += de_len;
 177                        continue;
 178                }
 179
 180                len = 0;
 181
 182                /* Handle the case of the '..' directory */
 183                if (de->name_len[0] == 1 && de->name[0] == 1) {
 184                        inode_number = parent_ino(filp->f_path.dentry);
 185                        if (filldir(dirent, "..", 2, filp->f_pos, inode_number, DT_DIR) < 0)
 186                                break;
 187                        filp->f_pos += de_len;
 188                        continue;
 189                }
 190
 191                /* Handle everything else.  Do name translation if there
 192                   is no Rock Ridge NM field. */
 193
 194                /*
 195                 * Do not report hidden files if so instructed, or associated
 196                 * files unless instructed to do so
 197                 */
 198                if ((sbi->s_hide && (de->flags[-sbi->s_high_sierra] & 1)) ||
 199                    (!sbi->s_showassoc &&
 200                                (de->flags[-sbi->s_high_sierra] & 4))) {
 201                        filp->f_pos += de_len;
 202                        continue;
 203                }
 204
 205                map = 1;
 206                if (sbi->s_rock) {
 207                        len = get_rock_ridge_filename(de, tmpname, inode);
 208                        if (len != 0) {         /* may be -1 */
 209                                p = tmpname;
 210                                map = 0;
 211                        }
 212                }
 213                if (map) {
 214#ifdef CONFIG_JOLIET
 215                        if (sbi->s_joliet_level) {
 216                                len = get_joliet_filename(de, tmpname, inode);
 217                                p = tmpname;
 218                        } else
 219#endif
 220                        if (sbi->s_mapping == 'a') {
 221                                len = get_acorn_filename(de, tmpname, inode);
 222                                p = tmpname;
 223                        } else
 224                        if (sbi->s_mapping == 'n') {
 225                                len = isofs_name_translate(de, tmpname, inode);
 226                                p = tmpname;
 227                        } else {
 228                                p = de->name;
 229                                len = de->name_len[0];
 230                        }
 231                }
 232                if (len > 0) {
 233                        if (filldir(dirent, p, len, filp->f_pos, inode_number, DT_UNKNOWN) < 0)
 234                                break;
 235                }
 236                filp->f_pos += de_len;
 237
 238                continue;
 239        }
 240        if (bh)
 241                brelse(bh);
 242        return 0;
 243}
 244
 245/*
 246 * Handle allocation of temporary space for name translation and
 247 * handling split directory entries.. The real work is done by
 248 * "do_isofs_readdir()".
 249 */
 250static int isofs_readdir(struct file *filp,
 251                void *dirent, filldir_t filldir)
 252{
 253        int result;
 254        char *tmpname;
 255        struct iso_directory_record *tmpde;
 256        struct inode *inode = filp->f_path.dentry->d_inode;
 257
 258        tmpname = (char *)__get_free_page(GFP_KERNEL);
 259        if (tmpname == NULL)
 260                return -ENOMEM;
 261
 262        lock_kernel();
 263        tmpde = (struct iso_directory_record *) (tmpname+1024);
 264
 265        result = do_isofs_readdir(inode, filp, dirent, filldir, tmpname, tmpde);
 266
 267        free_page((unsigned long) tmpname);
 268        unlock_kernel();
 269        return result;
 270}
 271
 272const struct file_operations isofs_dir_operations =
 273{
 274        .read = generic_read_dir,
 275        .readdir = isofs_readdir,
 276};
 277
 278/*
 279 * directories can handle most operations...
 280 */
 281const struct inode_operations isofs_dir_inode_operations =
 282{
 283        .lookup = isofs_lookup,
 284};
 285
 286
 287