linux/fs/isofs/dir.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 *  linux/fs/isofs/dir.c
   4 *
   5 *  (C) 1992, 1993, 1994  Eric Youngdale Modified for ISO 9660 filesystem.
   6 *
   7 *  (C) 1991  Linus Torvalds - minix filesystem
   8 *
   9 *  Steve Beynon                       : Missing last directory entries fixed
  10 *  (stephen@askone.demon.co.uk)      : 21st June 1996
  11 *
  12 *  isofs directory handling functions
  13 */
  14#include <linux/gfp.h>
  15#include "isofs.h"
  16
  17int isofs_name_translate(struct iso_directory_record *de, char *new, struct inode *inode)
  18{
  19        char * old = de->name;
  20        int len = de->name_len[0];
  21        int i;
  22
  23        for (i = 0; i < len; i++) {
  24                unsigned char c = old[i];
  25                if (!c)
  26                        break;
  27
  28                if (c >= 'A' && c <= 'Z')
  29                        c |= 0x20;      /* lower case */
  30
  31                /* Drop trailing '.;1' (ISO 9660:1988 7.5.1 requires period) */
  32                if (c == '.' && i == len - 3 && old[i + 1] == ';' && old[i + 2] == '1')
  33                        break;
  34
  35                /* Drop trailing ';1' */
  36                if (c == ';' && i == len - 2 && old[i + 1] == '1')
  37                        break;
  38
  39                /* Convert remaining ';' to '.' */
  40                /* Also '/' to '.' (broken Acorn-generated ISO9660 images) */
  41                if (c == ';' || c == '/')
  42                        c = '.';
  43
  44                new[i] = c;
  45        }
  46        return i;
  47}
  48
  49/* Acorn extensions written by Matthew Wilcox <willy@bofh.ai> 1998 */
  50int get_acorn_filename(struct iso_directory_record *de,
  51                            char *retname, struct inode *inode)
  52{
  53        int std;
  54        unsigned char *chr;
  55        int retnamlen = isofs_name_translate(de, retname, inode);
  56
  57        if (retnamlen == 0)
  58                return 0;
  59        std = sizeof(struct iso_directory_record) + de->name_len[0];
  60        if (std & 1)
  61                std++;
  62        if (de->length[0] - std != 32)
  63                return retnamlen;
  64        chr = ((unsigned char *) de) + std;
  65        if (strncmp(chr, "ARCHIMEDES", 10))
  66                return retnamlen;
  67        if ((*retname == '_') && ((chr[19] & 1) == 1))
  68                *retname = '!';
  69        if (((de->flags[0] & 2) == 0) && (chr[13] == 0xff)
  70                && ((chr[12] & 0xf0) == 0xf0)) {
  71                retname[retnamlen] = ',';
  72                sprintf(retname+retnamlen+1, "%3.3x",
  73                        ((chr[12] & 0xf) << 8) | chr[11]);
  74                retnamlen += 4;
  75        }
  76        return retnamlen;
  77}
  78
  79/*
  80 * This should _really_ be cleaned up some day..
  81 */
  82static int do_isofs_readdir(struct inode *inode, struct file *file,
  83                struct dir_context *ctx,
  84                char *tmpname, struct iso_directory_record *tmpde)
  85{
  86        unsigned long bufsize = ISOFS_BUFFER_SIZE(inode);
  87        unsigned char bufbits = ISOFS_BUFFER_BITS(inode);
  88        unsigned long block, offset, block_saved, offset_saved;
  89        unsigned long inode_number = 0; /* Quiet GCC */
  90        struct buffer_head *bh = NULL;
  91        int len;
  92        int map;
  93        int first_de = 1;
  94        char *p = NULL;         /* Quiet GCC */
  95        struct iso_directory_record *de;
  96        struct isofs_sb_info *sbi = ISOFS_SB(inode->i_sb);
  97
  98        offset = ctx->pos & (bufsize - 1);
  99        block = ctx->pos >> bufbits;
 100
 101        while (ctx->pos < inode->i_size) {
 102                int de_len;
 103
 104                if (!bh) {
 105                        bh = isofs_bread(inode, block);
 106                        if (!bh)
 107                                return 0;
 108                }
 109
 110                de = (struct iso_directory_record *) (bh->b_data + offset);
 111
 112                de_len = *(unsigned char *)de;
 113
 114                /*
 115                 * If the length byte is zero, we should move on to the next
 116                 * CDROM sector.  If we are at the end of the directory, we
 117                 * kick out of the while loop.
 118                 */
 119
 120                if (de_len == 0) {
 121                        brelse(bh);
 122                        bh = NULL;
 123                        ctx->pos = (ctx->pos + ISOFS_BLOCK_SIZE) & ~(ISOFS_BLOCK_SIZE - 1);
 124                        block = ctx->pos >> bufbits;
 125                        offset = 0;
 126                        continue;
 127                }
 128
 129                block_saved = block;
 130                offset_saved = offset;
 131                offset += de_len;
 132
 133                /* Make sure we have a full directory entry */
 134                if (offset >= bufsize) {
 135                        int slop = bufsize - offset + de_len;
 136                        memcpy(tmpde, de, slop);
 137                        offset &= bufsize - 1;
 138                        block++;
 139                        brelse(bh);
 140                        bh = NULL;
 141                        if (offset) {
 142                                bh = isofs_bread(inode, block);
 143                                if (!bh)
 144                                        return 0;
 145                                memcpy((void *) tmpde + slop, bh->b_data, offset);
 146                        }
 147                        de = tmpde;
 148                }
 149                /* Basic sanity check, whether name doesn't exceed dir entry */
 150                if (de_len < de->name_len[0] +
 151                                        sizeof(struct iso_directory_record)) {
 152                        printk(KERN_NOTICE "iso9660: Corrupted directory entry"
 153                               " in block %lu of inode %lu\n", block,
 154                               inode->i_ino);
 155                        return -EIO;
 156                }
 157
 158                if (first_de) {
 159                        isofs_normalize_block_and_offset(de,
 160                                                        &block_saved,
 161                                                        &offset_saved);
 162                        inode_number = isofs_get_ino(block_saved,
 163                                                        offset_saved, bufbits);
 164                }
 165
 166                if (de->flags[-sbi->s_high_sierra] & 0x80) {
 167                        first_de = 0;
 168                        ctx->pos += de_len;
 169                        continue;
 170                }
 171                first_de = 1;
 172
 173                /* Handle the case of the '.' directory */
 174                if (de->name_len[0] == 1 && de->name[0] == 0) {
 175                        if (!dir_emit_dot(file, ctx))
 176                                break;
 177                        ctx->pos += de_len;
 178                        continue;
 179                }
 180
 181                len = 0;
 182
 183                /* Handle the case of the '..' directory */
 184                if (de->name_len[0] == 1 && de->name[0] == 1) {
 185                        if (!dir_emit_dotdot(file, ctx))
 186                                break;
 187                        ctx->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                        ctx->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 (!dir_emit(ctx, p, len, inode_number, DT_UNKNOWN))
 234                                break;
 235                }
 236                ctx->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 *file, struct dir_context *ctx)
 251{
 252        int result;
 253        char *tmpname;
 254        struct iso_directory_record *tmpde;
 255        struct inode *inode = file_inode(file);
 256
 257        tmpname = (char *)__get_free_page(GFP_KERNEL);
 258        if (tmpname == NULL)
 259                return -ENOMEM;
 260
 261        tmpde = (struct iso_directory_record *) (tmpname+1024);
 262
 263        result = do_isofs_readdir(inode, file, ctx, tmpname, tmpde);
 264
 265        free_page((unsigned long) tmpname);
 266        return result;
 267}
 268
 269const struct file_operations isofs_dir_operations =
 270{
 271        .llseek = generic_file_llseek,
 272        .read = generic_read_dir,
 273        .iterate_shared = isofs_readdir,
 274};
 275
 276/*
 277 * directories can handle most operations...
 278 */
 279const struct inode_operations isofs_dir_inode_operations =
 280{
 281        .lookup = isofs_lookup,
 282};
 283
 284
 285