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