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