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