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