linux/fs/adfs/dir.c
<<
>>
Prefs
   1/*
   2 *  linux/fs/adfs/dir.c
   3 *
   4 *  Copyright (C) 1999-2000 Russell King
   5 *
   6 * This program is free software; you can redistribute it and/or modify
   7 * it under the terms of the GNU General Public License version 2 as
   8 * published by the Free Software Foundation.
   9 *
  10 *  Common directory handling for ADFS
  11 */
  12#include <linux/smp_lock.h>
  13#include "adfs.h"
  14
  15/*
  16 * For future.  This should probably be per-directory.
  17 */
  18static DEFINE_RWLOCK(adfs_dir_lock);
  19
  20static int
  21adfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
  22{
  23        struct inode *inode = filp->f_path.dentry->d_inode;
  24        struct super_block *sb = inode->i_sb;
  25        struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir;
  26        struct object_info obj;
  27        struct adfs_dir dir;
  28        int ret = 0;
  29
  30        lock_kernel();  
  31
  32        if (filp->f_pos >> 32)
  33                goto out;
  34
  35        ret = ops->read(sb, inode->i_ino, inode->i_size, &dir);
  36        if (ret)
  37                goto out;
  38
  39        switch ((unsigned long)filp->f_pos) {
  40        case 0:
  41                if (filldir(dirent, ".", 1, 0, inode->i_ino, DT_DIR) < 0)
  42                        goto free_out;
  43                filp->f_pos += 1;
  44
  45        case 1:
  46                if (filldir(dirent, "..", 2, 1, dir.parent_id, DT_DIR) < 0)
  47                        goto free_out;
  48                filp->f_pos += 1;
  49
  50        default:
  51                break;
  52        }
  53
  54        read_lock(&adfs_dir_lock);
  55
  56        ret = ops->setpos(&dir, filp->f_pos - 2);
  57        if (ret)
  58                goto unlock_out;
  59        while (ops->getnext(&dir, &obj) == 0) {
  60                if (filldir(dirent, obj.name, obj.name_len,
  61                            filp->f_pos, obj.file_id, DT_UNKNOWN) < 0)
  62                        goto unlock_out;
  63                filp->f_pos += 1;
  64        }
  65
  66unlock_out:
  67        read_unlock(&adfs_dir_lock);
  68
  69free_out:
  70        ops->free(&dir);
  71
  72out:
  73        unlock_kernel();
  74        return ret;
  75}
  76
  77int
  78adfs_dir_update(struct super_block *sb, struct object_info *obj, int wait)
  79{
  80        int ret = -EINVAL;
  81#ifdef CONFIG_ADFS_FS_RW
  82        struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir;
  83        struct adfs_dir dir;
  84
  85        printk(KERN_INFO "adfs_dir_update: object %06X in dir %06X\n",
  86                 obj->file_id, obj->parent_id);
  87
  88        if (!ops->update) {
  89                ret = -EINVAL;
  90                goto out;
  91        }
  92
  93        ret = ops->read(sb, obj->parent_id, 0, &dir);
  94        if (ret)
  95                goto out;
  96
  97        write_lock(&adfs_dir_lock);
  98        ret = ops->update(&dir, obj);
  99        write_unlock(&adfs_dir_lock);
 100
 101        if (wait) {
 102                int err = ops->sync(&dir);
 103                if (!ret)
 104                        ret = err;
 105        }
 106
 107        ops->free(&dir);
 108out:
 109#endif
 110        return ret;
 111}
 112
 113static int
 114adfs_match(struct qstr *name, struct object_info *obj)
 115{
 116        int i;
 117
 118        if (name->len != obj->name_len)
 119                return 0;
 120
 121        for (i = 0; i < name->len; i++) {
 122                char c1, c2;
 123
 124                c1 = name->name[i];
 125                c2 = obj->name[i];
 126
 127                if (c1 >= 'A' && c1 <= 'Z')
 128                        c1 += 'a' - 'A';
 129                if (c2 >= 'A' && c2 <= 'Z')
 130                        c2 += 'a' - 'A';
 131
 132                if (c1 != c2)
 133                        return 0;
 134        }
 135        return 1;
 136}
 137
 138static int
 139adfs_dir_lookup_byname(struct inode *inode, struct qstr *name, struct object_info *obj)
 140{
 141        struct super_block *sb = inode->i_sb;
 142        struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir;
 143        struct adfs_dir dir;
 144        int ret;
 145
 146        ret = ops->read(sb, inode->i_ino, inode->i_size, &dir);
 147        if (ret)
 148                goto out;
 149
 150        if (ADFS_I(inode)->parent_id != dir.parent_id) {
 151                adfs_error(sb, "parent directory changed under me! (%lx but got %lx)\n",
 152                           ADFS_I(inode)->parent_id, dir.parent_id);
 153                ret = -EIO;
 154                goto free_out;
 155        }
 156
 157        obj->parent_id = inode->i_ino;
 158
 159        /*
 160         * '.' is handled by reserved_lookup() in fs/namei.c
 161         */
 162        if (name->len == 2 && name->name[0] == '.' && name->name[1] == '.') {
 163                /*
 164                 * Currently unable to fill in the rest of 'obj',
 165                 * but this is better than nothing.  We need to
 166                 * ascend one level to find it's parent.
 167                 */
 168                obj->name_len = 0;
 169                obj->file_id  = obj->parent_id;
 170                goto free_out;
 171        }
 172
 173        read_lock(&adfs_dir_lock);
 174
 175        ret = ops->setpos(&dir, 0);
 176        if (ret)
 177                goto unlock_out;
 178
 179        ret = -ENOENT;
 180        while (ops->getnext(&dir, obj) == 0) {
 181                if (adfs_match(name, obj)) {
 182                        ret = 0;
 183                        break;
 184                }
 185        }
 186
 187unlock_out:
 188        read_unlock(&adfs_dir_lock);
 189
 190free_out:
 191        ops->free(&dir);
 192out:
 193        return ret;
 194}
 195
 196const struct file_operations adfs_dir_operations = {
 197        .read           = generic_read_dir,
 198        .llseek         = generic_file_llseek,
 199        .readdir        = adfs_readdir,
 200        .fsync          = simple_fsync,
 201};
 202
 203static int
 204adfs_hash(struct dentry *parent, struct qstr *qstr)
 205{
 206        const unsigned int name_len = ADFS_SB(parent->d_sb)->s_namelen;
 207        const unsigned char *name;
 208        unsigned long hash;
 209        int i;
 210
 211        if (qstr->len < name_len)
 212                return 0;
 213
 214        /*
 215         * Truncate the name in place, avoids
 216         * having to define a compare function.
 217         */
 218        qstr->len = i = name_len;
 219        name = qstr->name;
 220        hash = init_name_hash();
 221        while (i--) {
 222                char c;
 223
 224                c = *name++;
 225                if (c >= 'A' && c <= 'Z')
 226                        c += 'a' - 'A';
 227
 228                hash = partial_name_hash(c, hash);
 229        }
 230        qstr->hash = end_name_hash(hash);
 231
 232        return 0;
 233}
 234
 235/*
 236 * Compare two names, taking note of the name length
 237 * requirements of the underlying filesystem.
 238 */
 239static int
 240adfs_compare(struct dentry *parent, struct qstr *entry, struct qstr *name)
 241{
 242        int i;
 243
 244        if (entry->len != name->len)
 245                return 1;
 246
 247        for (i = 0; i < name->len; i++) {
 248                char a, b;
 249
 250                a = entry->name[i];
 251                b = name->name[i];
 252
 253                if (a >= 'A' && a <= 'Z')
 254                        a += 'a' - 'A';
 255                if (b >= 'A' && b <= 'Z')
 256                        b += 'a' - 'A';
 257
 258                if (a != b)
 259                        return 1;
 260        }
 261        return 0;
 262}
 263
 264const struct dentry_operations adfs_dentry_operations = {
 265        .d_hash         = adfs_hash,
 266        .d_compare      = adfs_compare,
 267};
 268
 269static struct dentry *
 270adfs_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd)
 271{
 272        struct inode *inode = NULL;
 273        struct object_info obj;
 274        int error;
 275
 276        dentry->d_op = &adfs_dentry_operations; 
 277        lock_kernel();
 278        error = adfs_dir_lookup_byname(dir, &dentry->d_name, &obj);
 279        if (error == 0) {
 280                error = -EACCES;
 281                /*
 282                 * This only returns NULL if get_empty_inode
 283                 * fails.
 284                 */
 285                inode = adfs_iget(dir->i_sb, &obj);
 286                if (inode)
 287                        error = 0;
 288        }
 289        unlock_kernel();
 290        d_add(dentry, inode);
 291        return ERR_PTR(error);
 292}
 293
 294/*
 295 * directories can handle most operations...
 296 */
 297const struct inode_operations adfs_dir_inode_operations = {
 298        .lookup         = adfs_lookup,
 299        .setattr        = adfs_notify_change,
 300};
 301
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.