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 inline unsigned long dir_pages(struct inode *inode)
  36{
  37        return (inode->i_size+PAGE_CACHE_SIZE-1)>>PAGE_CACHE_SHIFT;
  38}
  39
  40static unsigned last_entry(struct inode *inode, unsigned long page_nr)
  41{
  42        unsigned long last_byte = inode->i_size;
  43        last_byte -= page_nr << PAGE_CACHE_SHIFT;
  44        if (last_byte > PAGE_CACHE_SIZE)
  45                last_byte = PAGE_CACHE_SIZE;
  46        return last_byte / QNX6_DIR_ENTRY_SIZE;
  47}
  48
  49static struct qnx6_long_filename *qnx6_longname(struct super_block *sb,
  50                                         struct qnx6_long_dir_entry *de,
  51                                         struct page **p)
  52{
  53        struct qnx6_sb_info *sbi = QNX6_SB(sb);
  54        u32 s = fs32_to_cpu(sbi, de->de_long_inode); /* in block units */
  55        u32 n = s >> (PAGE_CACHE_SHIFT - sb->s_blocksize_bits); /* in pages */
  56        /* within page */
  57        u32 offs = (s << sb->s_blocksize_bits) & ~PAGE_CACHE_MASK;
  58        struct address_space *mapping = sbi->longfile->i_mapping;
  59        struct page *page = read_mapping_page(mapping, n, NULL);
  60        if (IS_ERR(page))
  61                return ERR_CAST(page);
  62        kmap(*p = page);
  63        return (struct qnx6_long_filename *)(page_address(page) + offs);
  64}
  65
  66static int qnx6_dir_longfilename(struct inode *inode,
  67                        struct qnx6_long_dir_entry *de,
  68                        void *dirent, loff_t pos,
  69                        unsigned de_inode, filldir_t filldir)
  70{
  71        struct qnx6_long_filename *lf;
  72        struct super_block *s = inode->i_sb;
  73        struct qnx6_sb_info *sbi = QNX6_SB(s);
  74        struct page *page;
  75        int lf_size;
  76
  77        if (de->de_size != 0xff) {
  78                /* error - long filename entries always have size 0xff
  79                   in direntry */
  80                printk(KERN_ERR "qnx6: invalid direntry size (%i).\n",
  81                                de->de_size);
  82                return 0;
  83        }
  84        lf = qnx6_longname(s, de, &page);
  85        if (IS_ERR(lf)) {
  86                printk(KERN_ERR "qnx6:Error reading longname\n");
  87                return 0;
  88        }
  89
  90        lf_size = fs16_to_cpu(sbi, lf->lf_size);
  91
  92        if (lf_size > QNX6_LONG_NAME_MAX) {
  93                QNX6DEBUG((KERN_INFO "file %s\n", lf->lf_fname));
  94                printk(KERN_ERR "qnx6:Filename too long (%i)\n", lf_size);
  95                qnx6_put_page(page);
  96                return 0;
  97        }
  98
  99        /* calc & validate longfilename checksum
 100           mmi 3g filesystem does not have that checksum */
 101        if (!test_opt(s, MMI_FS) && fs32_to_cpu(sbi, de->de_checksum) !=
 102                        qnx6_lfile_checksum(lf->lf_fname, lf_size))
 103                printk(KERN_INFO "qnx6: long filename checksum error.\n");
 104
 105        QNX6DEBUG((KERN_INFO "qnx6_readdir:%.*s inode:%u\n",
 106                                        lf_size, lf->lf_fname, de_inode));
 107        if (filldir(dirent, lf->lf_fname, lf_size, pos, de_inode,
 108                        DT_UNKNOWN) < 0) {
 109                qnx6_put_page(page);
 110                return 0;
 111        }
 112
 113        qnx6_put_page(page);
 114        /* success */
 115        return 1;
 116}
 117
 118static int qnx6_readdir(struct file *filp, void *dirent, filldir_t filldir)
 119{
 120        struct inode *inode = filp->f_path.dentry->d_inode;
 121        struct super_block *s = inode->i_sb;
 122        struct qnx6_sb_info *sbi = QNX6_SB(s);
 123        loff_t pos = filp->f_pos & (QNX6_DIR_ENTRY_SIZE - 1);
 124        unsigned long npages = dir_pages(inode);
 125        unsigned long n = pos >> PAGE_CACHE_SHIFT;
 126        unsigned start = (pos & ~PAGE_CACHE_MASK) / QNX6_DIR_ENTRY_SIZE;
 127        bool done = false;
 128
 129        if (filp->f_pos >= inode->i_size)
 130                return 0;
 131
 132        for ( ; !done && n < npages; n++, start = 0) {
 133                struct page *page = qnx6_get_page(inode, n);
 134                int limit = last_entry(inode, n);
 135                struct qnx6_dir_entry *de;
 136                int i = start;
 137
 138                if (IS_ERR(page)) {
 139                        printk(KERN_ERR "qnx6_readdir: read failed\n");
 140                        filp->f_pos = (n + 1) << PAGE_CACHE_SHIFT;
 141                        return PTR_ERR(page);
 142                }
 143                de = ((struct qnx6_dir_entry *)page_address(page)) + start;
 144                for (; i < limit; i++, de++, pos += QNX6_DIR_ENTRY_SIZE) {
 145                        int size = de->de_size;
 146                        u32 no_inode = fs32_to_cpu(sbi, de->de_inode);
 147
 148                        if (!no_inode || !size)
 149                                continue;
 150
 151                        if (size > QNX6_SHORT_NAME_MAX) {
 152                                /* long filename detected
 153                                   get the filename from long filename
 154                                   structure / block */
 155                                if (!qnx6_dir_longfilename(inode,
 156                                        (struct qnx6_long_dir_entry *)de,
 157                                        dirent, pos, no_inode,
 158                                        filldir)) {
 159                                        done = true;
 160                                        break;
 161                                }
 162                        } else {
 163                                QNX6DEBUG((KERN_INFO "qnx6_readdir:%.*s"
 164                                   " inode:%u\n", size, de->de_fname,
 165                                                        no_inode));
 166                                if (filldir(dirent, de->de_fname, size,
 167                                      pos, no_inode, DT_UNKNOWN)
 168                                        < 0) {
 169                                        done = true;
 170                                        break;
 171                                }
 172                        }
 173                }
 174                qnx6_put_page(page);
 175        }
 176        filp->f_pos = pos;
 177        return 0;
 178}
 179
 180/*
 181 * check if the long filename is correct.
 182 */
 183static unsigned qnx6_long_match(int len, const char *name,
 184                        struct qnx6_long_dir_entry *de, struct inode *dir)
 185{
 186        struct super_block *s = dir->i_sb;
 187        struct qnx6_sb_info *sbi = QNX6_SB(s);
 188        struct page *page;
 189        int thislen;
 190        struct qnx6_long_filename *lf = qnx6_longname(s, de, &page);
 191
 192        if (IS_ERR(lf))
 193                return 0;
 194
 195        thislen = fs16_to_cpu(sbi, lf->lf_size);
 196        if (len != thislen) {
 197                qnx6_put_page(page);
 198                return 0;
 199        }
 200        if (memcmp(name, lf->lf_fname, len) == 0) {
 201                qnx6_put_page(page);
 202                return fs32_to_cpu(sbi, de->de_inode);
 203        }
 204        qnx6_put_page(page);
 205        return 0;
 206}
 207
 208/*
 209 * check if the filename is correct.
 210 */
 211static unsigned qnx6_match(struct super_block *s, int len, const char *name,
 212                        struct qnx6_dir_entry *de)
 213{
 214        struct qnx6_sb_info *sbi = QNX6_SB(s);
 215        if (memcmp(name, de->de_fname, len) == 0)
 216                return fs32_to_cpu(sbi, de->de_inode);
 217        return 0;
 218}
 219
 220
 221unsigned qnx6_find_entry(int len, struct inode *dir, const char *name,
 222                         struct page **res_page)
 223{
 224        struct super_block *s = dir->i_sb;
 225        struct qnx6_inode_info *ei = QNX6_I(dir);
 226        struct page *page = NULL;
 227        unsigned long start, n;
 228        unsigned long npages = dir_pages(dir);
 229        unsigned ino;
 230        struct qnx6_dir_entry *de;
 231        struct qnx6_long_dir_entry *lde;
 232
 233        *res_page = NULL;
 234
 235        if (npages == 0)
 236                return 0;
 237        start = ei->i_dir_start_lookup;
 238        if (start >= npages)
 239                start = 0;
 240        n = start;
 241
 242        do {
 243                page = qnx6_get_page(dir, n);
 244                if (!IS_ERR(page)) {
 245                        int limit = last_entry(dir, n);
 246                        int i;
 247
 248                        de = (struct qnx6_dir_entry *)page_address(page);
 249                        for (i = 0; i < limit; i++, de++) {
 250                                if (len <= QNX6_SHORT_NAME_MAX) {
 251                                        /* short filename */
 252                                        if (len != de->de_size)
 253                                                continue;
 254                                        ino = qnx6_match(s, len, name, de);
 255                                        if (ino)
 256                                                goto found;
 257                                } else if (de->de_size == 0xff) {
 258                                        /* deal with long filename */
 259                                        lde = (struct qnx6_long_dir_entry *)de;
 260                                        ino = qnx6_long_match(len,
 261                                                                name, lde, dir);
 262                                        if (ino)
 263                                                goto found;
 264                                } else
 265                                        printk(KERN_ERR "qnx6: undefined "
 266                                                "filename size in inode.\n");
 267                        }
 268                        qnx6_put_page(page);
 269                }
 270
 271                if (++n >= npages)
 272                        n = 0;
 273        } while (n != start);
 274        return 0;
 275
 276found:
 277        *res_page = page;
 278        ei->i_dir_start_lookup = n;
 279        return ino;
 280}
 281
 282const struct file_operations qnx6_dir_operations = {
 283        .llseek         = generic_file_llseek,
 284        .read           = generic_read_dir,
 285        .readdir        = qnx6_readdir,
 286        .fsync          = generic_file_fsync,
 287};
 288
 289const struct inode_operations qnx6_dir_inode_operations = {
 290        .lookup         = qnx6_lookup,
 291};
 292
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.