linux/fs/affs/amigaffs.c
<<
>>
Prefs
   1/*
   2 *  linux/fs/affs/amigaffs.c
   3 *
   4 *  (c) 1996  Hans-Joachim Widmaier - Rewritten
   5 *
   6 *  (C) 1993  Ray Burr - Amiga FFS filesystem.
   7 *
   8 *  Please send bug reports to: hjw@zvw.de
   9 */
  10
  11#include "affs.h"
  12
  13extern struct timezone sys_tz;
  14
  15static char ErrorBuffer[256];
  16
  17/*
  18 * Functions for accessing Amiga-FFS structures.
  19 */
  20
  21
  22/* Insert a header block bh into the directory dir
  23 * caller must hold AFFS_DIR->i_hash_lock!
  24 */
  25
  26int
  27affs_insert_hash(struct inode *dir, struct buffer_head *bh)
  28{
  29        struct super_block *sb = dir->i_sb;
  30        struct buffer_head *dir_bh;
  31        u32 ino, hash_ino;
  32        int offset;
  33
  34        ino = bh->b_blocknr;
  35        offset = affs_hash_name(sb, AFFS_TAIL(sb, bh)->name + 1, AFFS_TAIL(sb, bh)->name[0]);
  36
  37        pr_debug("AFFS: insert_hash(dir=%u, ino=%d)\n", (u32)dir->i_ino, ino);
  38
  39        dir_bh = affs_bread(sb, dir->i_ino);
  40        if (!dir_bh)
  41                return -EIO;
  42
  43        hash_ino = be32_to_cpu(AFFS_HEAD(dir_bh)->table[offset]);
  44        while (hash_ino) {
  45                affs_brelse(dir_bh);
  46                dir_bh = affs_bread(sb, hash_ino);
  47                if (!dir_bh)
  48                        return -EIO;
  49                hash_ino = be32_to_cpu(AFFS_TAIL(sb, dir_bh)->hash_chain);
  50        }
  51        AFFS_TAIL(sb, bh)->parent = cpu_to_be32(dir->i_ino);
  52        AFFS_TAIL(sb, bh)->hash_chain = 0;
  53        affs_fix_checksum(sb, bh);
  54
  55        if (dir->i_ino == dir_bh->b_blocknr)
  56                AFFS_HEAD(dir_bh)->table[offset] = cpu_to_be32(ino);
  57        else
  58                AFFS_TAIL(sb, dir_bh)->hash_chain = cpu_to_be32(ino);
  59
  60        affs_adjust_checksum(dir_bh, ino);
  61        mark_buffer_dirty_inode(dir_bh, dir);
  62        affs_brelse(dir_bh);
  63
  64        dir->i_mtime = dir->i_ctime = CURRENT_TIME_SEC;
  65        dir->i_version++;
  66        mark_inode_dirty(dir);
  67
  68        return 0;
  69}
  70
  71/* Remove a header block from its directory.
  72 * caller must hold AFFS_DIR->i_hash_lock!
  73 */
  74
  75int
  76affs_remove_hash(struct inode *dir, struct buffer_head *rem_bh)
  77{
  78        struct super_block *sb;
  79        struct buffer_head *bh;
  80        u32 rem_ino, hash_ino;
  81        __be32 ino;
  82        int offset, retval;
  83
  84        sb = dir->i_sb;
  85        rem_ino = rem_bh->b_blocknr;
  86        offset = affs_hash_name(sb, AFFS_TAIL(sb, rem_bh)->name+1, AFFS_TAIL(sb, rem_bh)->name[0]);
  87        pr_debug("AFFS: remove_hash(dir=%d, ino=%d, hashval=%d)\n", (u32)dir->i_ino, rem_ino, offset);
  88
  89        bh = affs_bread(sb, dir->i_ino);
  90        if (!bh)
  91                return -EIO;
  92
  93        retval = -ENOENT;
  94        hash_ino = be32_to_cpu(AFFS_HEAD(bh)->table[offset]);
  95        while (hash_ino) {
  96                if (hash_ino == rem_ino) {
  97                        ino = AFFS_TAIL(sb, rem_bh)->hash_chain;
  98                        if (dir->i_ino == bh->b_blocknr)
  99                                AFFS_HEAD(bh)->table[offset] = ino;
 100                        else
 101                                AFFS_TAIL(sb, bh)->hash_chain = ino;
 102                        affs_adjust_checksum(bh, be32_to_cpu(ino) - hash_ino);
 103                        mark_buffer_dirty_inode(bh, dir);
 104                        AFFS_TAIL(sb, rem_bh)->parent = 0;
 105                        retval = 0;
 106                        break;
 107                }
 108                affs_brelse(bh);
 109                bh = affs_bread(sb, hash_ino);
 110                if (!bh)
 111                        return -EIO;
 112                hash_ino = be32_to_cpu(AFFS_TAIL(sb, bh)->hash_chain);
 113        }
 114
 115        affs_brelse(bh);
 116
 117        dir->i_mtime = dir->i_ctime = CURRENT_TIME_SEC;
 118        dir->i_version++;
 119        mark_inode_dirty(dir);
 120
 121        return retval;
 122}
 123
 124static void
 125affs_fix_dcache(struct inode *inode, u32 entry_ino)
 126{
 127        struct dentry *dentry;
 128        spin_lock(&inode->i_lock);
 129        hlist_for_each_entry(dentry, &inode->i_dentry, d_alias) {
 130                if (entry_ino == (u32)(long)dentry->d_fsdata) {
 131                        dentry->d_fsdata = (void *)inode->i_ino;
 132                        break;
 133                }
 134        }
 135        spin_unlock(&inode->i_lock);
 136}
 137
 138
 139/* Remove header from link chain */
 140
 141static int
 142affs_remove_link(struct dentry *dentry)
 143{
 144        struct inode *dir, *inode = dentry->d_inode;
 145        struct super_block *sb = inode->i_sb;
 146        struct buffer_head *bh = NULL, *link_bh = NULL;
 147        u32 link_ino, ino;
 148        int retval;
 149
 150        pr_debug("AFFS: remove_link(key=%ld)\n", inode->i_ino);
 151        retval = -EIO;
 152        bh = affs_bread(sb, inode->i_ino);
 153        if (!bh)
 154                goto done;
 155
 156        link_ino = (u32)(long)dentry->d_fsdata;
 157        if (inode->i_ino == link_ino) {
 158                /* we can't remove the head of the link, as its blocknr is still used as ino,
 159                 * so we remove the block of the first link instead.
 160                 */ 
 161                link_ino = be32_to_cpu(AFFS_TAIL(sb, bh)->link_chain);
 162                link_bh = affs_bread(sb, link_ino);
 163                if (!link_bh)
 164                        goto done;
 165
 166                dir = affs_iget(sb, be32_to_cpu(AFFS_TAIL(sb, link_bh)->parent));
 167                if (IS_ERR(dir)) {
 168                        retval = PTR_ERR(dir);
 169                        goto done;
 170                }
 171
 172                affs_lock_dir(dir);
 173                /*
 174                 * if there's a dentry for that block, make it
 175                 * refer to inode itself.
 176                 */
 177                affs_fix_dcache(inode, link_ino);
 178                retval = affs_remove_hash(dir, link_bh);
 179                if (retval) {
 180                        affs_unlock_dir(dir);
 181                        goto done;
 182                }
 183                mark_buffer_dirty_inode(link_bh, inode);
 184
 185                memcpy(AFFS_TAIL(sb, bh)->name, AFFS_TAIL(sb, link_bh)->name, 32);
 186                retval = affs_insert_hash(dir, bh);
 187                if (retval) {
 188                        affs_unlock_dir(dir);
 189                        goto done;
 190                }
 191                mark_buffer_dirty_inode(bh, inode);
 192
 193                affs_unlock_dir(dir);
 194                iput(dir);
 195        } else {
 196                link_bh = affs_bread(sb, link_ino);
 197                if (!link_bh)
 198                        goto done;
 199        }
 200
 201        while ((ino = be32_to_cpu(AFFS_TAIL(sb, bh)->link_chain)) != 0) {
 202                if (ino == link_ino) {
 203                        __be32 ino2 = AFFS_TAIL(sb, link_bh)->link_chain;
 204                        AFFS_TAIL(sb, bh)->link_chain = ino2;
 205                        affs_adjust_checksum(bh, be32_to_cpu(ino2) - link_ino);
 206                        mark_buffer_dirty_inode(bh, inode);
 207                        retval = 0;
 208                        /* Fix the link count, if bh is a normal header block without links */
 209                        switch (be32_to_cpu(AFFS_TAIL(sb, bh)->stype)) {
 210                        case ST_LINKDIR:
 211                        case ST_LINKFILE:
 212                                break;
 213                        default:
 214                                if (!AFFS_TAIL(sb, bh)->link_chain)
 215                                        set_nlink(inode, 1);
 216                        }
 217                        affs_free_block(sb, link_ino);
 218                        goto done;
 219                }
 220                affs_brelse(bh);
 221                bh = affs_bread(sb, ino);
 222                if (!bh)
 223                        goto done;
 224        }
 225        retval = -ENOENT;
 226done:
 227        affs_brelse(link_bh);
 228        affs_brelse(bh);
 229        return retval;
 230}
 231
 232
 233static int
 234affs_empty_dir(struct inode *inode)
 235{
 236        struct super_block *sb = inode->i_sb;
 237        struct buffer_head *bh;
 238        int retval, size;
 239
 240        retval = -EIO;
 241        bh = affs_bread(sb, inode->i_ino);
 242        if (!bh)
 243                goto done;
 244
 245        retval = -ENOTEMPTY;
 246        for (size = AFFS_SB(sb)->s_hashsize - 1; size >= 0; size--)
 247                if (AFFS_HEAD(bh)->table[size])
 248                        goto not_empty;
 249        retval = 0;
 250not_empty:
 251        affs_brelse(bh);
 252done:
 253        return retval;
 254}
 255
 256
 257/* Remove a filesystem object. If the object to be removed has
 258 * links to it, one of the links must be changed to inherit
 259 * the file or directory. As above, any inode will do.
 260 * The buffer will not be freed. If the header is a link, the
 261 * block will be marked as free.
 262 * This function returns a negative error number in case of
 263 * an error, else 0 if the inode is to be deleted or 1 if not.
 264 */
 265
 266int
 267affs_remove_header(struct dentry *dentry)
 268{
 269        struct super_block *sb;
 270        struct inode *inode, *dir;
 271        struct buffer_head *bh = NULL;
 272        int retval;
 273
 274        dir = dentry->d_parent->d_inode;
 275        sb = dir->i_sb;
 276
 277        retval = -ENOENT;
 278        inode = dentry->d_inode;
 279        if (!inode)
 280                goto done;
 281
 282        pr_debug("AFFS: remove_header(key=%ld)\n", inode->i_ino);
 283        retval = -EIO;
 284        bh = affs_bread(sb, (u32)(long)dentry->d_fsdata);
 285        if (!bh)
 286                goto done;
 287
 288        affs_lock_link(inode);
 289        affs_lock_dir(dir);
 290        switch (be32_to_cpu(AFFS_TAIL(sb, bh)->stype)) {
 291        case ST_USERDIR:
 292                /* if we ever want to support links to dirs
 293                 * i_hash_lock of the inode must only be
 294                 * taken after some checks
 295                 */
 296                affs_lock_dir(inode);
 297                retval = affs_empty_dir(inode);
 298                affs_unlock_dir(inode);
 299                if (retval)
 300                        goto done_unlock;
 301                break;
 302        default:
 303                break;
 304        }
 305
 306        retval = affs_remove_hash(dir, bh);
 307        if (retval)
 308                goto done_unlock;
 309        mark_buffer_dirty_inode(bh, inode);
 310
 311        affs_unlock_dir(dir);
 312
 313        if (inode->i_nlink > 1)
 314                retval = affs_remove_link(dentry);
 315        else
 316                clear_nlink(inode);
 317        affs_unlock_link(inode);
 318        inode->i_ctime = CURRENT_TIME_SEC;
 319        mark_inode_dirty(inode);
 320
 321done:
 322        affs_brelse(bh);
 323        return retval;
 324
 325done_unlock:
 326        affs_unlock_dir(dir);
 327        affs_unlock_link(inode);
 328        goto done;
 329}
 330
 331/* Checksum a block, do various consistency checks and optionally return
 332   the blocks type number.  DATA points to the block.  If their pointers
 333   are non-null, *PTYPE and *STYPE are set to the primary and secondary
 334   block types respectively, *HASHSIZE is set to the size of the hashtable
 335   (which lets us calculate the block size).
 336   Returns non-zero if the block is not consistent. */
 337
 338u32
 339affs_checksum_block(struct super_block *sb, struct buffer_head *bh)
 340{
 341        __be32 *ptr = (__be32 *)bh->b_data;
 342        u32 sum;
 343        int bsize;
 344
 345        sum = 0;
 346        for (bsize = sb->s_blocksize / sizeof(__be32); bsize > 0; bsize--)
 347                sum += be32_to_cpu(*ptr++);
 348        return sum;
 349}
 350
 351/*
 352 * Calculate the checksum of a disk block and store it
 353 * at the indicated position.
 354 */
 355
 356void
 357affs_fix_checksum(struct super_block *sb, struct buffer_head *bh)
 358{
 359        int cnt = sb->s_blocksize / sizeof(__be32);
 360        __be32 *ptr = (__be32 *)bh->b_data;
 361        u32 checksum;
 362        __be32 *checksumptr;
 363
 364        checksumptr = ptr + 5;
 365        *checksumptr = 0;
 366        for (checksum = 0; cnt > 0; ptr++, cnt--)
 367                checksum += be32_to_cpu(*ptr);
 368        *checksumptr = cpu_to_be32(-checksum);
 369}
 370
 371void
 372secs_to_datestamp(time_t secs, struct affs_date *ds)
 373{
 374        u32      days;
 375        u32      minute;
 376
 377        secs -= sys_tz.tz_minuteswest * 60 + ((8 * 365 + 2) * 24 * 60 * 60);
 378        if (secs < 0)
 379                secs = 0;
 380        days    = secs / 86400;
 381        secs   -= days * 86400;
 382        minute  = secs / 60;
 383        secs   -= minute * 60;
 384
 385        ds->days = cpu_to_be32(days);
 386        ds->mins = cpu_to_be32(minute);
 387        ds->ticks = cpu_to_be32(secs * 50);
 388}
 389
 390umode_t
 391prot_to_mode(u32 prot)
 392{
 393        umode_t mode = 0;
 394
 395        if (!(prot & FIBF_NOWRITE))
 396                mode |= S_IWUSR;
 397        if (!(prot & FIBF_NOREAD))
 398                mode |= S_IRUSR;
 399        if (!(prot & FIBF_NOEXECUTE))
 400                mode |= S_IXUSR;
 401        if (prot & FIBF_GRP_WRITE)
 402                mode |= S_IWGRP;
 403        if (prot & FIBF_GRP_READ)
 404                mode |= S_IRGRP;
 405        if (prot & FIBF_GRP_EXECUTE)
 406                mode |= S_IXGRP;
 407        if (prot & FIBF_OTR_WRITE)
 408                mode |= S_IWOTH;
 409        if (prot & FIBF_OTR_READ)
 410                mode |= S_IROTH;
 411        if (prot & FIBF_OTR_EXECUTE)
 412                mode |= S_IXOTH;
 413
 414        return mode;
 415}
 416
 417void
 418mode_to_prot(struct inode *inode)
 419{
 420        u32 prot = AFFS_I(inode)->i_protect;
 421        umode_t mode = inode->i_mode;
 422
 423        if (!(mode & S_IXUSR))
 424                prot |= FIBF_NOEXECUTE;
 425        if (!(mode & S_IRUSR))
 426                prot |= FIBF_NOREAD;
 427        if (!(mode & S_IWUSR))
 428                prot |= FIBF_NOWRITE;
 429        if (mode & S_IXGRP)
 430                prot |= FIBF_GRP_EXECUTE;
 431        if (mode & S_IRGRP)
 432                prot |= FIBF_GRP_READ;
 433        if (mode & S_IWGRP)
 434                prot |= FIBF_GRP_WRITE;
 435        if (mode & S_IXOTH)
 436                prot |= FIBF_OTR_EXECUTE;
 437        if (mode & S_IROTH)
 438                prot |= FIBF_OTR_READ;
 439        if (mode & S_IWOTH)
 440                prot |= FIBF_OTR_WRITE;
 441
 442        AFFS_I(inode)->i_protect = prot;
 443}
 444
 445void
 446affs_error(struct super_block *sb, const char *function, const char *fmt, ...)
 447{
 448        va_list  args;
 449
 450        va_start(args,fmt);
 451        vsnprintf(ErrorBuffer,sizeof(ErrorBuffer),fmt,args);
 452        va_end(args);
 453
 454        printk(KERN_CRIT "AFFS error (device %s): %s(): %s\n", sb->s_id,
 455                function,ErrorBuffer);
 456        if (!(sb->s_flags & MS_RDONLY))
 457                printk(KERN_WARNING "AFFS: Remounting filesystem read-only\n");
 458        sb->s_flags |= MS_RDONLY;
 459}
 460
 461void
 462affs_warning(struct super_block *sb, const char *function, const char *fmt, ...)
 463{
 464        va_list  args;
 465
 466        va_start(args,fmt);
 467        vsnprintf(ErrorBuffer,sizeof(ErrorBuffer),fmt,args);
 468        va_end(args);
 469
 470        printk(KERN_WARNING "AFFS warning (device %s): %s(): %s\n", sb->s_id,
 471                function,ErrorBuffer);
 472}
 473
 474/* Check if the name is valid for a affs object. */
 475
 476int
 477affs_check_name(const unsigned char *name, int len)
 478{
 479        int      i;
 480
 481        if (len > 30)
 482#ifdef AFFS_NO_TRUNCATE
 483                return -ENAMETOOLONG;
 484#else
 485                len = 30;
 486#endif
 487
 488        for (i = 0; i < len; i++) {
 489                if (name[i] < ' ' || name[i] == ':'
 490                    || (name[i] > 0x7e && name[i] < 0xa0))
 491                        return -EINVAL;
 492        }
 493
 494        return 0;
 495}
 496
 497/* This function copies name to bstr, with at most 30
 498 * characters length. The bstr will be prepended by
 499 * a length byte.
 500 * NOTE: The name will must be already checked by
 501 *       affs_check_name()!
 502 */
 503
 504int
 505affs_copy_name(unsigned char *bstr, struct dentry *dentry)
 506{
 507        int len = min(dentry->d_name.len, 30u);
 508
 509        *bstr++ = len;
 510        memcpy(bstr, dentry->d_name.name, len);
 511        return len;
 512}
 513
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.