linux/fs/affs/amigaffs.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 *  linux/fs/affs/amigaffs.c
   4 *
   5 *  (c) 1996  Hans-Joachim Widmaier - Rewritten
   6 *
   7 *  (C) 1993  Ray Burr - Amiga FFS filesystem.
   8 *
   9 *  Please send bug reports to: hjw@zvw.de
  10 */
  11
  12#include <linux/math64.h>
  13#include <linux/iversion.h>
  14#include "affs.h"
  15
  16/*
  17 * Functions for accessing Amiga-FFS structures.
  18 */
  19
  20
  21/* Insert a header block bh into the directory dir
  22 * caller must hold AFFS_DIR->i_hash_lock!
  23 */
  24
  25int
  26affs_insert_hash(struct inode *dir, struct buffer_head *bh)
  27{
  28        struct super_block *sb = dir->i_sb;
  29        struct buffer_head *dir_bh;
  30        u32 ino, hash_ino;
  31        int offset;
  32
  33        ino = bh->b_blocknr;
  34        offset = affs_hash_name(sb, AFFS_TAIL(sb, bh)->name + 1, AFFS_TAIL(sb, bh)->name[0]);
  35
  36        pr_debug("%s(dir=%lu, ino=%d)\n", __func__, dir->i_ino, ino);
  37
  38        dir_bh = affs_bread(sb, dir->i_ino);
  39        if (!dir_bh)
  40                return -EIO;
  41
  42        hash_ino = be32_to_cpu(AFFS_HEAD(dir_bh)->table[offset]);
  43        while (hash_ino) {
  44                affs_brelse(dir_bh);
  45                dir_bh = affs_bread(sb, hash_ino);
  46                if (!dir_bh)
  47                        return -EIO;
  48                hash_ino = be32_to_cpu(AFFS_TAIL(sb, dir_bh)->hash_chain);
  49        }
  50        AFFS_TAIL(sb, bh)->parent = cpu_to_be32(dir->i_ino);
  51        AFFS_TAIL(sb, bh)->hash_chain = 0;
  52        affs_fix_checksum(sb, bh);
  53
  54        if (dir->i_ino == dir_bh->b_blocknr)
  55                AFFS_HEAD(dir_bh)->table[offset] = cpu_to_be32(ino);
  56        else
  57                AFFS_TAIL(sb, dir_bh)->hash_chain = cpu_to_be32(ino);
  58
  59        affs_adjust_checksum(dir_bh, ino);
  60        mark_buffer_dirty_inode(dir_bh, dir);
  61        affs_brelse(dir_bh);
  62
  63        dir->i_mtime = dir->i_ctime = current_time(dir);
  64        inode_inc_iversion(dir);
  65        mark_inode_dirty(dir);
  66
  67        return 0;
  68}
  69
  70/* Remove a header block from its directory.
  71 * caller must hold AFFS_DIR->i_hash_lock!
  72 */
  73
  74int
  75affs_remove_hash(struct inode *dir, struct buffer_head *rem_bh)
  76{
  77        struct super_block *sb;
  78        struct buffer_head *bh;
  79        u32 rem_ino, hash_ino;
  80        __be32 ino;
  81        int offset, retval;
  82
  83        sb = dir->i_sb;
  84        rem_ino = rem_bh->b_blocknr;
  85        offset = affs_hash_name(sb, AFFS_TAIL(sb, rem_bh)->name+1, AFFS_TAIL(sb, rem_bh)->name[0]);
  86        pr_debug("%s(dir=%lu, ino=%d, hashval=%d)\n", __func__, dir->i_ino,
  87                 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(dir);
 118        inode_inc_iversion(dir);
 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_u.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 = d_inode(dentry);
 145        struct super_block *sb = inode->i_sb;
 146        struct buffer_head *bh, *link_bh = NULL;
 147        u32 link_ino, ino;
 148        int retval;
 149
 150        pr_debug("%s(key=%ld)\n", __func__, 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 = d_inode(dentry->d_parent);
 275        sb = dir->i_sb;
 276
 277        retval = -ENOENT;
 278        inode = d_inode(dentry);
 279        if (!inode)
 280                goto done;
 281
 282        pr_debug("%s(key=%ld)\n", __func__, 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(inode);
 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
 372affs_secs_to_datestamp(time64_t secs, struct affs_date *ds)
 373{
 374        u32      days;
 375        u32      minute;
 376        s32      rem;
 377
 378        secs -= sys_tz.tz_minuteswest * 60 + AFFS_EPOCH_DELTA;
 379        if (secs < 0)
 380                secs = 0;
 381        days    = div_s64_rem(secs, 86400, &rem);
 382        minute  = rem / 60;
 383        rem    -= minute * 60;
 384
 385        ds->days = cpu_to_be32(days);
 386        ds->mins = cpu_to_be32(minute);
 387        ds->ticks = cpu_to_be32(rem * 50);
 388}
 389
 390umode_t
 391affs_prot_to_mode(u32 prot)
 392{
 393        umode_t mode = 0;
 394
 395        if (!(prot & FIBF_NOWRITE))
 396                mode |= 0200;
 397        if (!(prot & FIBF_NOREAD))
 398                mode |= 0400;
 399        if (!(prot & FIBF_NOEXECUTE))
 400                mode |= 0100;
 401        if (prot & FIBF_GRP_WRITE)
 402                mode |= 0020;
 403        if (prot & FIBF_GRP_READ)
 404                mode |= 0040;
 405        if (prot & FIBF_GRP_EXECUTE)
 406                mode |= 0010;
 407        if (prot & FIBF_OTR_WRITE)
 408                mode |= 0002;
 409        if (prot & FIBF_OTR_READ)
 410                mode |= 0004;
 411        if (prot & FIBF_OTR_EXECUTE)
 412                mode |= 0001;
 413
 414        return mode;
 415}
 416
 417void
 418affs_mode_to_prot(struct inode *inode)
 419{
 420        u32 prot = AFFS_I(inode)->i_protect;
 421        umode_t mode = inode->i_mode;
 422
 423        /*
 424         * First, clear all RWED bits for owner, group, other.
 425         * Then, recalculate them afresh.
 426         *
 427         * We'll always clear the delete-inhibit bit for the owner, as that is
 428         * the classic single-user mode AmigaOS protection bit and we need to
 429         * stay compatible with all scenarios.
 430         *
 431         * Since multi-user AmigaOS is an extension, we'll only set the
 432         * delete-allow bit if any of the other bits in the same user class
 433         * (group/other) are used.
 434         */
 435        prot &= ~(FIBF_NOEXECUTE | FIBF_NOREAD
 436                  | FIBF_NOWRITE | FIBF_NODELETE
 437                  | FIBF_GRP_EXECUTE | FIBF_GRP_READ
 438                  | FIBF_GRP_WRITE   | FIBF_GRP_DELETE
 439                  | FIBF_OTR_EXECUTE | FIBF_OTR_READ
 440                  | FIBF_OTR_WRITE   | FIBF_OTR_DELETE);
 441
 442        /* Classic single-user AmigaOS flags. These are inverted. */
 443        if (!(mode & 0100))
 444                prot |= FIBF_NOEXECUTE;
 445        if (!(mode & 0400))
 446                prot |= FIBF_NOREAD;
 447        if (!(mode & 0200))
 448                prot |= FIBF_NOWRITE;
 449
 450        /* Multi-user extended flags. Not inverted. */
 451        if (mode & 0010)
 452                prot |= FIBF_GRP_EXECUTE;
 453        if (mode & 0040)
 454                prot |= FIBF_GRP_READ;
 455        if (mode & 0020)
 456                prot |= FIBF_GRP_WRITE;
 457        if (mode & 0070)
 458                prot |= FIBF_GRP_DELETE;
 459
 460        if (mode & 0001)
 461                prot |= FIBF_OTR_EXECUTE;
 462        if (mode & 0004)
 463                prot |= FIBF_OTR_READ;
 464        if (mode & 0002)
 465                prot |= FIBF_OTR_WRITE;
 466        if (mode & 0007)
 467                prot |= FIBF_OTR_DELETE;
 468
 469        AFFS_I(inode)->i_protect = prot;
 470}
 471
 472void
 473affs_error(struct super_block *sb, const char *function, const char *fmt, ...)
 474{
 475        struct va_format vaf;
 476        va_list args;
 477
 478        va_start(args, fmt);
 479        vaf.fmt = fmt;
 480        vaf.va = &args;
 481        pr_crit("error (device %s): %s(): %pV\n", sb->s_id, function, &vaf);
 482        if (!sb_rdonly(sb))
 483                pr_warn("Remounting filesystem read-only\n");
 484        sb->s_flags |= SB_RDONLY;
 485        va_end(args);
 486}
 487
 488void
 489affs_warning(struct super_block *sb, const char *function, const char *fmt, ...)
 490{
 491        struct va_format vaf;
 492        va_list args;
 493
 494        va_start(args, fmt);
 495        vaf.fmt = fmt;
 496        vaf.va = &args;
 497        pr_warn("(device %s): %s(): %pV\n", sb->s_id, function, &vaf);
 498        va_end(args);
 499}
 500
 501bool
 502affs_nofilenametruncate(const struct dentry *dentry)
 503{
 504        return affs_test_opt(AFFS_SB(dentry->d_sb)->s_flags, SF_NO_TRUNCATE);
 505}
 506
 507/* Check if the name is valid for a affs object. */
 508
 509int
 510affs_check_name(const unsigned char *name, int len, bool notruncate)
 511{
 512        int      i;
 513
 514        if (len > AFFSNAMEMAX) {
 515                if (notruncate)
 516                        return -ENAMETOOLONG;
 517                len = AFFSNAMEMAX;
 518        }
 519        for (i = 0; i < len; i++) {
 520                if (name[i] < ' ' || name[i] == ':'
 521                    || (name[i] > 0x7e && name[i] < 0xa0))
 522                        return -EINVAL;
 523        }
 524
 525        return 0;
 526}
 527
 528/* This function copies name to bstr, with at most 30
 529 * characters length. The bstr will be prepended by
 530 * a length byte.
 531 * NOTE: The name will must be already checked by
 532 *       affs_check_name()!
 533 */
 534
 535int
 536affs_copy_name(unsigned char *bstr, struct dentry *dentry)
 537{
 538        u32 len = min(dentry->d_name.len, AFFSNAMEMAX);
 539
 540        *bstr++ = len;
 541        memcpy(bstr, dentry->d_name.name, len);
 542        return len;
 543}
 544