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@infradead.org> 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                        brelse(bh);
 156                        return -EIO;
 157                }
 158
 159                if (first_de) {
 160                        isofs_normalize_block_and_offset(de,
 161                                                        &block_saved,
 162                                                        &offset_saved);
 163                        inode_number = isofs_get_ino(block_saved,
 164                                                        offset_saved, bufbits);
 165                }
 166
 167                if (de->flags[-sbi->s_high_sierra] & 0x80) {
 168                        first_de = 0;
 169                        ctx->pos += de_len;
 170                        continue;
 171                }
 172                first_de = 1;
 173
 174                /* Handle the case of the '.' directory */
 175                if (de->name_len[0] == 1 && de->name[0] == 0) {
 176                        if (!dir_emit_dot(file, ctx))
 177                                break;
 178                        ctx->pos += de_len;
 179                        continue;
 180                }
 181
 182                len = 0;
 183
 184                /* Handle the case of the '..' directory */
 185                if (de->name_len[0] == 1 && de->name[0] == 1) {
 186                        if (!dir_emit_dotdot(file, ctx))
 187                                break;
 188                        ctx->pos += de_len;
 189                        continue;
 190                }
 191
 192                /* Handle everything else.  Do name translation if there
 193                   is no Rock Ridge NM field. */
 194
 195                /*
 196                 * Do not report hidden files if so instructed, or associated
 197                 * files unless instructed to do so
 198                 */
 199                if ((sbi->s_hide && (de->flags[-sbi->s_high_sierra] & 1)) ||
 200                    (!sbi->s_showassoc &&
 201                                (de->flags[-sbi->s_high_sierra] & 4))) {
 202                        ctx->pos += de_len;
 203                        continue;
 204                }
 205
 206                map = 1;
 207                if (sbi->s_rock) {
 208                        len = get_rock_ridge_filename(de, tmpname, inode);
 209                        if (len != 0) {         /* may be -1 */
 210                                p = tmpname;
 211                                map = 0;
 212                        }
 213                }
 214                if (map) {
 215#ifdef CONFIG_JOLIET
 216                        if (sbi->s_joliet_level) {
 217                                len = get_joliet_filename(de, tmpname, inode);
 218                                p = tmpname;
 219                        } else
 220#endif
 221                        if (sbi->s_mapping == 'a') {
 222                                len = get_acorn_filename(de, tmpname, inode);
 223                                p = tmpname;
 224                        } else
 225                        if (sbi->s_mapping == 'n') {
 226                                len = isofs_name_translate(de, tmpname, inode);
 227                                p = tmpname;
 228                        } else {
 229                                p = de->name;
 230                                len = de->name_len[0];
 231                        }
 232                }
 233                if (len > 0) {
 234                        if (!dir_emit(ctx, p, len, inode_number, DT_UNKNOWN))
 235                                break;
 236                }
 237                ctx->pos += de_len;
 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_shared = 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