linux/fs/qnx6/dir.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * QNX6 file system, Linux implementation.
   4 *
   5 * Version : 1.0.0
   6 *
   7 * History :
   8 *
   9 * 01-02-2012 by Kai Bankett (chaosman@ontika.net) : first release.
  10 * 16-02-2012 pagemap extension by Al Viro
  11 *
  12 */
  13
  14#include "qnx6.h"
  15
  16static unsigned qnx6_lfile_checksum(char *name, unsigned size)
  17{
  18        unsigned crc = 0;
  19        char *end = name + size;
  20        while (name < end) {
  21                crc = ((crc >> 1) + *(name++)) ^
  22                        ((crc & 0x00000001) ? 0x80000000 : 0);
  23        }
  24        return crc;
  25}
  26
  27static struct page *qnx6_get_page(struct inode *dir, unsigned long n)
  28{
  29        struct address_space *mapping = dir->i_mapping;
  30        struct page *page = read_mapping_page(mapping, n, NULL);
  31        if (!IS_ERR(page))
  32                kmap(page);
  33        return page;
  34}
  35
  36static unsigned last_entry(struct inode *inode, unsigned long page_nr)
  37{
  38        unsigned long last_byte = inode->i_size;
  39        last_byte -= page_nr << PAGE_SHIFT;
  40        if (last_byte > PAGE_SIZE)
  41                last_byte = PAGE_SIZE;
  42        return last_byte / QNX6_DIR_ENTRY_SIZE;
  43}
  44
  45static struct qnx6_long_filename *qnx6_longname(struct super_block *sb,
  46                                         struct qnx6_long_dir_entry *de,
  47                                         struct page **p)
  48{
  49        struct qnx6_sb_info *sbi = QNX6_SB(sb);
  50        u32 s = fs32_to_cpu(sbi, de->de_long_inode); /* in block units */
  51        u32 n = s >> (PAGE_SHIFT - sb->s_blocksize_bits); /* in pages */
  52        /* within page */
  53        u32 offs = (s << sb->s_blocksize_bits) & ~PAGE_MASK;
  54        struct address_space *mapping = sbi->longfile->i_mapping;
  55        struct page *page = read_mapping_page(mapping, n, NULL);
  56        if (IS_ERR(page))
  57                return ERR_CAST(page);
  58        kmap(*p = page);
  59        return (struct qnx6_long_filename *)(page_address(page) + offs);
  60}
  61
  62static int qnx6_dir_longfilename(struct inode *inode,
  63                        struct qnx6_long_dir_entry *de,
  64                        struct dir_context *ctx,
  65                        unsigned de_inode)
  66{
  67        struct qnx6_long_filename *lf;
  68        struct super_block *s = inode->i_sb;
  69        struct qnx6_sb_info *sbi = QNX6_SB(s);
  70        struct page *page;
  71        int lf_size;
  72
  73        if (de->de_size != 0xff) {
  74                /* error - long filename entries always have size 0xff
  75                   in direntry */
  76                pr_err("invalid direntry size (%i).\n", de->de_size);
  77                return 0;
  78        }
  79        lf = qnx6_longname(s, de, &page);
  80        if (IS_ERR(lf)) {
  81                pr_err("Error reading longname\n");
  82                return 0;
  83        }
  84
  85        lf_size = fs16_to_cpu(sbi, lf->lf_size);
  86
  87        if (lf_size > QNX6_LONG_NAME_MAX) {
  88                pr_debug("file %s\n", lf->lf_fname);
  89                pr_err("Filename too long (%i)\n", lf_size);
  90                qnx6_put_page(page);
  91                return 0;
  92        }
  93
  94        /* calc & validate longfilename checksum
  95           mmi 3g filesystem does not have that checksum */
  96        if (!test_opt(s, MMI_FS) && fs32_to_cpu(sbi, de->de_checksum) !=
  97                        qnx6_lfile_checksum(lf->lf_fname, lf_size))
  98                pr_info("long filename checksum error.\n");
  99
 100        pr_debug("qnx6_readdir:%.*s inode:%u\n",
 101                 lf_size, lf->lf_fname, de_inode);
 102        if (!dir_emit(ctx, lf->lf_fname, lf_size, de_inode, DT_UNKNOWN)) {
 103                qnx6_put_page(page);
 104                return 0;
 105        }
 106
 107        qnx6_put_page(page);
 108        /* success */
 109        return 1;
 110}
 111
 112static int qnx6_readdir(struct file *file, struct dir_context *ctx)
 113{
 114        struct inode *inode = file_inode(file);
 115        struct super_block *s = inode->i_sb;
 116        struct qnx6_sb_info *sbi = QNX6_SB(s);
 117        loff_t pos = ctx->pos & ~(QNX6_DIR_ENTRY_SIZE - 1);
 118        unsigned long npages = dir_pages(inode);
 119        unsigned long n = pos >> PAGE_SHIFT;
 120        unsigned start = (pos & ~PAGE_MASK) / QNX6_DIR_ENTRY_SIZE;
 121        bool done = false;
 122
 123        ctx->pos = pos;
 124        if (ctx->pos >= inode->i_size)
 125                return 0;
 126
 127        for ( ; !done && n < npages; n++, start = 0) {
 128                struct page *page = qnx6_get_page(inode, n);
 129                int limit = last_entry(inode, n);
 130                struct qnx6_dir_entry *de;
 131                int i = start;
 132
 133                if (IS_ERR(page)) {
 134                        pr_err("%s(): read failed\n", __func__);
 135                        ctx->pos = (n + 1) << PAGE_SHIFT;
 136                        return PTR_ERR(page);
 137                }
 138                de = ((struct qnx6_dir_entry *)page_address(page)) + start;
 139                for (; i < limit; i++, de++, ctx->pos += QNX6_DIR_ENTRY_SIZE) {
 140                        int size = de->de_size;
 141                        u32 no_inode = fs32_to_cpu(sbi, de->de_inode);
 142
 143                        if (!no_inode || !size)
 144                                continue;
 145
 146                        if (size > QNX6_SHORT_NAME_MAX) {
 147                                /* long filename detected
 148                                   get the filename from long filename
 149                                   structure / block */
 150                                if (!qnx6_dir_longfilename(inode,
 151                                        (struct qnx6_long_dir_entry *)de,
 152                                        ctx, no_inode)) {
 153                                        done = true;
 154                                        break;
 155                                }
 156                        } else {
 157                                pr_debug("%s():%.*s inode:%u\n",
 158                                         __func__, size, de->de_fname,
 159                                         no_inode);
 160                                if (!dir_emit(ctx, de->de_fname, size,
 161                                      no_inode, DT_UNKNOWN)) {
 162                                        done = true;
 163                                        break;
 164                                }
 165                        }
 166                }
 167                qnx6_put_page(page);
 168        }
 169        return 0;
 170}
 171
 172/*
 173 * check if the long filename is correct.
 174 */
 175static unsigned qnx6_long_match(int len, const char *name,
 176                        struct qnx6_long_dir_entry *de, struct inode *dir)
 177{
 178        struct super_block *s = dir->i_sb;
 179        struct qnx6_sb_info *sbi = QNX6_SB(s);
 180        struct page *page;
 181        int thislen;
 182        struct qnx6_long_filename *lf = qnx6_longname(s, de, &page);
 183
 184        if (IS_ERR(lf))
 185                return 0;
 186
 187        thislen = fs16_to_cpu(sbi, lf->lf_size);
 188        if (len != thislen) {
 189                qnx6_put_page(page);
 190                return 0;
 191        }
 192        if (memcmp(name, lf->lf_fname, len) == 0) {
 193                qnx6_put_page(page);
 194                return fs32_to_cpu(sbi, de->de_inode);
 195        }
 196        qnx6_put_page(page);
 197        return 0;
 198}
 199
 200/*
 201 * check if the filename is correct.
 202 */
 203static unsigned qnx6_match(struct super_block *s, int len, const char *name,
 204                        struct qnx6_dir_entry *de)
 205{
 206        struct qnx6_sb_info *sbi = QNX6_SB(s);
 207        if (memcmp(name, de->de_fname, len) == 0)
 208                return fs32_to_cpu(sbi, de->de_inode);
 209        return 0;
 210}
 211
 212
 213unsigned qnx6_find_entry(int len, struct inode *dir, const char *name,
 214                         struct page **res_page)
 215{
 216        struct super_block *s = dir->i_sb;
 217        struct qnx6_inode_info *ei = QNX6_I(dir);
 218        struct page *page = NULL;
 219        unsigned long start, n;
 220        unsigned long npages = dir_pages(dir);
 221        unsigned ino;
 222        struct qnx6_dir_entry *de;
 223        struct qnx6_long_dir_entry *lde;
 224
 225        *res_page = NULL;
 226
 227        if (npages == 0)
 228                return 0;
 229        start = ei->i_dir_start_lookup;
 230        if (start >= npages)
 231                start = 0;
 232        n = start;
 233
 234        do {
 235                page = qnx6_get_page(dir, n);
 236                if (!IS_ERR(page)) {
 237                        int limit = last_entry(dir, n);
 238                        int i;
 239
 240                        de = (struct qnx6_dir_entry *)page_address(page);
 241                        for (i = 0; i < limit; i++, de++) {
 242                                if (len <= QNX6_SHORT_NAME_MAX) {
 243                                        /* short filename */
 244                                        if (len != de->de_size)
 245                                                continue;
 246                                        ino = qnx6_match(s, len, name, de);
 247                                        if (ino)
 248                                                goto found;
 249                                } else if (de->de_size == 0xff) {
 250                                        /* deal with long filename */
 251                                        lde = (struct qnx6_long_dir_entry *)de;
 252                                        ino = qnx6_long_match(len,
 253                                                                name, lde, dir);
 254                                        if (ino)
 255                                                goto found;
 256                                } else
 257                                        pr_err("undefined filename size in inode.\n");
 258                        }
 259                        qnx6_put_page(page);
 260                }
 261
 262                if (++n >= npages)
 263                        n = 0;
 264        } while (n != start);
 265        return 0;
 266
 267found:
 268        *res_page = page;
 269        ei->i_dir_start_lookup = n;
 270        return ino;
 271}
 272
 273const struct file_operations qnx6_dir_operations = {
 274        .llseek         = generic_file_llseek,
 275        .read           = generic_read_dir,
 276        .iterate_shared = qnx6_readdir,
 277        .fsync          = generic_file_fsync,
 278};
 279
 280const struct inode_operations qnx6_dir_inode_operations = {
 281        .lookup         = qnx6_lookup,
 282};
 283