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