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/gfp.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 *file,
  82                struct dir_context *ctx,
  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 = ctx->pos & (bufsize - 1);
  98        block = ctx->pos >> bufbits;
  99
 100        while (ctx->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                        ctx->pos = (ctx->pos + ISOFS_BLOCK_SIZE) & ~(ISOFS_BLOCK_SIZE - 1);
 123                        block = ctx->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                        ctx->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 (!dir_emit_dot(file, ctx))
 175                                break;
 176                        ctx->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                        if (!dir_emit_dotdot(file, ctx))
 185                                break;
 186                        ctx->pos += de_len;
 187                        continue;
 188                }
 189
 190                /* Handle everything else.  Do name translation if there
 191                   is no Rock Ridge NM field. */
 192
 193                /*
 194                 * Do not report hidden files if so instructed, or associated
 195                 * files unless instructed to do so
 196                 */
 197                if ((sbi->s_hide && (de->flags[-sbi->s_high_sierra] & 1)) ||
 198                    (!sbi->s_showassoc &&
 199                                (de->flags[-sbi->s_high_sierra] & 4))) {
 200                        ctx->pos += de_len;
 201                        continue;
 202                }
 203
 204                map = 1;
 205                if (sbi->s_rock) {
 206                        len = get_rock_ridge_filename(de, tmpname, inode);
 207                        if (len != 0) {         /* may be -1 */
 208                                p = tmpname;
 209                                map = 0;
 210                        }
 211                }
 212                if (map) {
 213#ifdef CONFIG_JOLIET
 214                        if (sbi->s_joliet_level) {
 215                                len = get_joliet_filename(de, tmpname, inode);
 216                                p = tmpname;
 217                        } else
 218#endif
 219                        if (sbi->s_mapping == 'a') {
 220                                len = get_acorn_filename(de, tmpname, inode);
 221                                p = tmpname;
 222                        } else
 223                        if (sbi->s_mapping == 'n') {
 224                                len = isofs_name_translate(de, tmpname, inode);
 225                                p = tmpname;
 226                        } else {
 227                                p = de->name;
 228                                len = de->name_len[0];
 229                        }
 230                }
 231                if (len > 0) {
 232                        if (!dir_emit(ctx, p, len, inode_number, DT_UNKNOWN))
 233                                break;
 234                }
 235                ctx->pos += de_len;
 236
 237                continue;
 238        }
 239        if (bh)
 240                brelse(bh);
 241        return 0;
 242}
 243
 244/*
 245 * Handle allocation of temporary space for name translation and
 246 * handling split directory entries.. The real work is done by
 247 * "do_isofs_readdir()".
 248 */
 249static int isofs_readdir(struct file *file, struct dir_context *ctx)
 250{
 251        int result;
 252        char *tmpname;
 253        struct iso_directory_record *tmpde;
 254        struct inode *inode = file_inode(file);
 255
 256        tmpname = (char *)__get_free_page(GFP_KERNEL);
 257        if (tmpname == NULL)
 258                return -ENOMEM;
 259
 260        tmpde = (struct iso_directory_record *) (tmpname+1024);
 261
 262        result = do_isofs_readdir(inode, file, ctx, tmpname, tmpde);
 263
 264        free_page((unsigned long) tmpname);
 265        return result;
 266}
 267
 268const struct file_operations isofs_dir_operations =
 269{
 270        .llseek = generic_file_llseek,
 271        .read = generic_read_dir,
 272        .iterate = isofs_readdir,
 273};
 274
 275/*
 276 * directories can handle most operations...
 277 */
 278const struct inode_operations isofs_dir_inode_operations =
 279{
 280        .lookup = isofs_lookup,
 281};
 282
 283
 284