linux/fs/affs/namei.c
<<
>>
Prefs
   1/*
   2 *  linux/fs/affs/namei.c
   3 *
   4 *  (c) 1996  Hans-Joachim Widmaier - Rewritten
   5 *
   6 *  (C) 1993  Ray Burr - Modified for Amiga FFS filesystem.
   7 *
   8 *  (C) 1991  Linus Torvalds - minix filesystem
   9 */
  10
  11#include "affs.h"
  12
  13typedef int (*toupper_t)(int);
  14
  15static int       affs_toupper(int ch);
  16static int       affs_hash_dentry(struct dentry *, struct qstr *);
  17static int       affs_compare_dentry(struct dentry *, struct qstr *, struct qstr *);
  18static int       affs_intl_toupper(int ch);
  19static int       affs_intl_hash_dentry(struct dentry *, struct qstr *);
  20static int       affs_intl_compare_dentry(struct dentry *, struct qstr *, struct qstr *);
  21
  22const struct dentry_operations affs_dentry_operations = {
  23        .d_hash         = affs_hash_dentry,
  24        .d_compare      = affs_compare_dentry,
  25};
  26
  27static const struct dentry_operations affs_intl_dentry_operations = {
  28        .d_hash         = affs_intl_hash_dentry,
  29        .d_compare      = affs_intl_compare_dentry,
  30};
  31
  32
  33/* Simple toupper() for DOS\1 */
  34
  35static int
  36affs_toupper(int ch)
  37{
  38        return ch >= 'a' && ch <= 'z' ? ch -= ('a' - 'A') : ch;
  39}
  40
  41/* International toupper() for DOS\3 ("international") */
  42
  43static int
  44affs_intl_toupper(int ch)
  45{
  46        return (ch >= 'a' && ch <= 'z') || (ch >= 0xE0
  47                && ch <= 0xFE && ch != 0xF7) ?
  48                ch - ('a' - 'A') : ch;
  49}
  50
  51static inline toupper_t
  52affs_get_toupper(struct super_block *sb)
  53{
  54        return AFFS_SB(sb)->s_flags & SF_INTL ? affs_intl_toupper : affs_toupper;
  55}
  56
  57/*
  58 * Note: the dentry argument is the parent dentry.
  59 */
  60static inline int
  61__affs_hash_dentry(struct dentry *dentry, struct qstr *qstr, toupper_t toupper)
  62{
  63        const u8 *name = qstr->name;
  64        unsigned long hash;
  65        int i;
  66
  67        i = affs_check_name(qstr->name,qstr->len);
  68        if (i)
  69                return i;
  70
  71        hash = init_name_hash();
  72        i = min(qstr->len, 30u);
  73        for (; i > 0; name++, i--)
  74                hash = partial_name_hash(toupper(*name), hash);
  75        qstr->hash = end_name_hash(hash);
  76
  77        return 0;
  78}
  79
  80static int
  81affs_hash_dentry(struct dentry *dentry, struct qstr *qstr)
  82{
  83        return __affs_hash_dentry(dentry, qstr, affs_toupper);
  84}
  85static int
  86affs_intl_hash_dentry(struct dentry *dentry, struct qstr *qstr)
  87{
  88        return __affs_hash_dentry(dentry, qstr, affs_intl_toupper);
  89}
  90
  91static inline int
  92__affs_compare_dentry(struct dentry *dentry, struct qstr *a, struct qstr *b, toupper_t toupper)
  93{
  94        const u8 *aname = a->name;
  95        const u8 *bname = b->name;
  96        int len;
  97
  98        /* 'a' is the qstr of an already existing dentry, so the name
  99         * must be valid. 'b' must be validated first.
 100         */
 101
 102        if (affs_check_name(b->name,b->len))
 103                return 1;
 104
 105        /* If the names are longer than the allowed 30 chars,
 106         * the excess is ignored, so their length may differ.
 107         */
 108        len = a->len;
 109        if (len >= 30) {
 110                if (b->len < 30)
 111                        return 1;
 112                len = 30;
 113        } else if (len != b->len)
 114                return 1;
 115
 116        for (; len > 0; len--)
 117                if (toupper(*aname++) != toupper(*bname++))
 118                        return 1;
 119
 120        return 0;
 121}
 122
 123static int
 124affs_compare_dentry(struct dentry *dentry, struct qstr *a, struct qstr *b)
 125{
 126        return __affs_compare_dentry(dentry, a, b, affs_toupper);
 127}
 128static int
 129affs_intl_compare_dentry(struct dentry *dentry, struct qstr *a, struct qstr *b)
 130{
 131        return __affs_compare_dentry(dentry, a, b, affs_intl_toupper);
 132}
 133
 134/*
 135 * NOTE! unlike strncmp, affs_match returns 1 for success, 0 for failure.
 136 */
 137
 138static inline int
 139affs_match(struct dentry *dentry, const u8 *name2, toupper_t toupper)
 140{
 141        const u8 *name = dentry->d_name.name;
 142        int len = dentry->d_name.len;
 143
 144        if (len >= 30) {
 145                if (*name2 < 30)
 146                        return 0;
 147                len = 30;
 148        } else if (len != *name2)
 149                return 0;
 150
 151        for (name2++; len > 0; len--)
 152                if (toupper(*name++) != toupper(*name2++))
 153                        return 0;
 154        return 1;
 155}
 156
 157int
 158affs_hash_name(struct super_block *sb, const u8 *name, unsigned int len)
 159{
 160        toupper_t toupper = affs_get_toupper(sb);
 161        int hash;
 162
 163        hash = len = min(len, 30u);
 164        for (; len > 0; len--)
 165                hash = (hash * 13 + toupper(*name++)) & 0x7ff;
 166
 167        return hash % AFFS_SB(sb)->s_hashsize;
 168}
 169
 170static struct buffer_head *
 171affs_find_entry(struct inode *dir, struct dentry *dentry)
 172{
 173        struct super_block *sb = dir->i_sb;
 174        struct buffer_head *bh;
 175        toupper_t toupper = affs_get_toupper(sb);
 176        u32 key;
 177
 178        pr_debug("AFFS: find_entry(\"%.*s\")\n", (int)dentry->d_name.len, dentry->d_name.name);
 179
 180        bh = affs_bread(sb, dir->i_ino);
 181        if (!bh)
 182                return ERR_PTR(-EIO);
 183
 184        key = be32_to_cpu(AFFS_HEAD(bh)->table[affs_hash_name(sb, dentry->d_name.name, dentry->d_name.len)]);
 185
 186        for (;;) {
 187                affs_brelse(bh);
 188                if (key == 0)
 189                        return NULL;
 190                bh = affs_bread(sb, key);
 191                if (!bh)
 192                        return ERR_PTR(-EIO);
 193                if (affs_match(dentry, AFFS_TAIL(sb, bh)->name, toupper))
 194                        return bh;
 195                key = be32_to_cpu(AFFS_TAIL(sb, bh)->hash_chain);
 196        }
 197}
 198
 199struct dentry *
 200affs_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd)
 201{
 202        struct super_block *sb = dir->i_sb;
 203        struct buffer_head *bh;
 204        struct inode *inode = NULL;
 205
 206        pr_debug("AFFS: lookup(\"%.*s\")\n",(int)dentry->d_name.len,dentry->d_name.name);
 207
 208        affs_lock_dir(dir);
 209        bh = affs_find_entry(dir, dentry);
 210        affs_unlock_dir(dir);
 211        if (IS_ERR(bh))
 212                return ERR_CAST(bh);
 213        if (bh) {
 214                u32 ino = bh->b_blocknr;
 215
 216                /* store the real header ino in d_fsdata for faster lookups */
 217                dentry->d_fsdata = (void *)(long)ino;
 218                switch (be32_to_cpu(AFFS_TAIL(sb, bh)->stype)) {
 219                //link to dirs disabled
 220                //case ST_LINKDIR:
 221                case ST_LINKFILE:
 222                        ino = be32_to_cpu(AFFS_TAIL(sb, bh)->original);
 223                }
 224                affs_brelse(bh);
 225                inode = affs_iget(sb, ino);
 226                if (IS_ERR(inode))
 227                        return ERR_PTR(PTR_ERR(inode));
 228        }
 229        dentry->d_op = AFFS_SB(sb)->s_flags & SF_INTL ? &affs_intl_dentry_operations : &affs_dentry_operations;
 230        d_add(dentry, inode);
 231        return NULL;
 232}
 233
 234int
 235affs_unlink(struct inode *dir, struct dentry *dentry)
 236{
 237        pr_debug("AFFS: unlink(dir=%d, %lu \"%.*s\")\n", (u32)dir->i_ino,
 238                 dentry->d_inode->i_ino,
 239                 (int)dentry->d_name.len, dentry->d_name.name);
 240
 241        return affs_remove_header(dentry);
 242}
 243
 244int
 245affs_create(struct inode *dir, struct dentry *dentry, int mode, struct nameidata *nd)
 246{
 247        struct super_block *sb = dir->i_sb;
 248        struct inode    *inode;
 249        int              error;
 250
 251        pr_debug("AFFS: create(%lu,\"%.*s\",0%o)\n",dir->i_ino,(int)dentry->d_name.len,
 252                 dentry->d_name.name,mode);
 253
 254        inode = affs_new_inode(dir);
 255        if (!inode)
 256                return -ENOSPC;
 257
 258        inode->i_mode = mode;
 259        mode_to_prot(inode);
 260        mark_inode_dirty(inode);
 261
 262        inode->i_op = &affs_file_inode_operations;
 263        inode->i_fop = &affs_file_operations;
 264        inode->i_mapping->a_ops = (AFFS_SB(sb)->s_flags & SF_OFS) ? &affs_aops_ofs : &affs_aops;
 265        error = affs_add_entry(dir, inode, dentry, ST_FILE);
 266        if (error) {
 267                inode->i_nlink = 0;
 268                iput(inode);
 269                return error;
 270        }
 271        return 0;
 272}
 273
 274int
 275affs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
 276{
 277        struct inode            *inode;
 278        int                      error;
 279
 280        pr_debug("AFFS: mkdir(%lu,\"%.*s\",0%o)\n",dir->i_ino,
 281                 (int)dentry->d_name.len,dentry->d_name.name,mode);
 282
 283        inode = affs_new_inode(dir);
 284        if (!inode)
 285                return -ENOSPC;
 286
 287        inode->i_mode = S_IFDIR | mode;
 288        mode_to_prot(inode);
 289
 290        inode->i_op = &affs_dir_inode_operations;
 291        inode->i_fop = &affs_dir_operations;
 292
 293        error = affs_add_entry(dir, inode, dentry, ST_USERDIR);
 294        if (error) {
 295                inode->i_nlink = 0;
 296                mark_inode_dirty(inode);
 297                iput(inode);
 298                return error;
 299        }
 300        return 0;
 301}
 302
 303int
 304affs_rmdir(struct inode *dir, struct dentry *dentry)
 305{
 306        pr_debug("AFFS: rmdir(dir=%u, %lu \"%.*s\")\n", (u32)dir->i_ino,
 307                 dentry->d_inode->i_ino,
 308                 (int)dentry->d_name.len, dentry->d_name.name);
 309
 310        return affs_remove_header(dentry);
 311}
 312
 313int
 314affs_symlink(struct inode *dir, struct dentry *dentry, const char *symname)
 315{
 316        struct super_block      *sb = dir->i_sb;
 317        struct buffer_head      *bh;
 318        struct inode            *inode;
 319        char                    *p;
 320        int                      i, maxlen, error;
 321        char                     c, lc;
 322
 323        pr_debug("AFFS: symlink(%lu,\"%.*s\" -> \"%s\")\n",dir->i_ino,
 324                 (int)dentry->d_name.len,dentry->d_name.name,symname);
 325
 326        maxlen = AFFS_SB(sb)->s_hashsize * sizeof(u32) - 1;
 327        inode  = affs_new_inode(dir);
 328        if (!inode)
 329                return -ENOSPC;
 330
 331        inode->i_op = &affs_symlink_inode_operations;
 332        inode->i_data.a_ops = &affs_symlink_aops;
 333        inode->i_mode = S_IFLNK | 0777;
 334        mode_to_prot(inode);
 335
 336        error = -EIO;
 337        bh = affs_bread(sb, inode->i_ino);
 338        if (!bh)
 339                goto err;
 340        i  = 0;
 341        p  = (char *)AFFS_HEAD(bh)->table;
 342        lc = '/';
 343        if (*symname == '/') {
 344                while (*symname == '/')
 345                        symname++;
 346                while (AFFS_SB(sb)->s_volume[i])        /* Cannot overflow */
 347                        *p++ = AFFS_SB(sb)->s_volume[i++];
 348        }
 349        while (i < maxlen && (c = *symname++)) {
 350                if (c == '.' && lc == '/' && *symname == '.' && symname[1] == '/') {
 351                        *p++ = '/';
 352                        i++;
 353                        symname += 2;
 354                        lc = '/';
 355                } else if (c == '.' && lc == '/' && *symname == '/') {
 356                        symname++;
 357                        lc = '/';
 358                } else {
 359                        *p++ = c;
 360                        lc   = c;
 361                        i++;
 362                }
 363                if (lc == '/')
 364                        while (*symname == '/')
 365                                symname++;
 366        }
 367        *p = 0;
 368        mark_buffer_dirty_inode(bh, inode);
 369        affs_brelse(bh);
 370        mark_inode_dirty(inode);
 371
 372        error = affs_add_entry(dir, inode, dentry, ST_SOFTLINK);
 373        if (error)
 374                goto err;
 375
 376        return 0;
 377
 378err:
 379        inode->i_nlink = 0;
 380        mark_inode_dirty(inode);
 381        iput(inode);
 382        return error;
 383}
 384
 385int
 386affs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry)
 387{
 388        struct inode *inode = old_dentry->d_inode;
 389
 390        pr_debug("AFFS: link(%u, %u, \"%.*s\")\n", (u32)inode->i_ino, (u32)dir->i_ino,
 391                 (int)dentry->d_name.len,dentry->d_name.name);
 392
 393        return affs_add_entry(dir, inode, dentry, ST_LINKFILE);
 394}
 395
 396int
 397affs_rename(struct inode *old_dir, struct dentry *old_dentry,
 398            struct inode *new_dir, struct dentry *new_dentry)
 399{
 400        struct super_block *sb = old_dir->i_sb;
 401        struct buffer_head *bh = NULL;
 402        int retval;
 403
 404        pr_debug("AFFS: rename(old=%u,\"%*s\" to new=%u,\"%*s\")\n",
 405                 (u32)old_dir->i_ino, (int)old_dentry->d_name.len, old_dentry->d_name.name,
 406                 (u32)new_dir->i_ino, (int)new_dentry->d_name.len, new_dentry->d_name.name);
 407
 408        retval = affs_check_name(new_dentry->d_name.name,new_dentry->d_name.len);
 409        if (retval)
 410                return retval;
 411
 412        /* Unlink destination if it already exists */
 413        if (new_dentry->d_inode) {
 414                retval = affs_remove_header(new_dentry);
 415                if (retval)
 416                        return retval;
 417        }
 418
 419        bh = affs_bread(sb, old_dentry->d_inode->i_ino);
 420        if (!bh)
 421                return -EIO;
 422
 423        /* Remove header from its parent directory. */
 424        affs_lock_dir(old_dir);
 425        retval = affs_remove_hash(old_dir, bh);
 426        affs_unlock_dir(old_dir);
 427        if (retval)
 428                goto done;
 429
 430        /* And insert it into the new directory with the new name. */
 431        affs_copy_name(AFFS_TAIL(sb, bh)->name, new_dentry);
 432        affs_fix_checksum(sb, bh);
 433        affs_lock_dir(new_dir);
 434        retval = affs_insert_hash(new_dir, bh);
 435        affs_unlock_dir(new_dir);
 436        /* TODO: move it back to old_dir, if error? */
 437
 438done:
 439        mark_buffer_dirty_inode(bh, retval ? old_dir : new_dir);
 440        affs_brelse(bh);
 441        return retval;
 442}
 443