linux/fs/minix/itree_common.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/* Generic part */
   3
   4typedef struct {
   5        block_t *p;
   6        block_t key;
   7        struct buffer_head *bh;
   8} Indirect;
   9
  10static DEFINE_RWLOCK(pointers_lock);
  11
  12static inline void add_chain(Indirect *p, struct buffer_head *bh, block_t *v)
  13{
  14        p->key = *(p->p = v);
  15        p->bh = bh;
  16}
  17
  18static inline int verify_chain(Indirect *from, Indirect *to)
  19{
  20        while (from <= to && from->key == *from->p)
  21                from++;
  22        return (from > to);
  23}
  24
  25static inline block_t *block_end(struct buffer_head *bh)
  26{
  27        return (block_t *)((char*)bh->b_data + bh->b_size);
  28}
  29
  30static inline Indirect *get_branch(struct inode *inode,
  31                                        int depth,
  32                                        int *offsets,
  33                                        Indirect chain[DEPTH],
  34                                        int *err)
  35{
  36        struct super_block *sb = inode->i_sb;
  37        Indirect *p = chain;
  38        struct buffer_head *bh;
  39
  40        *err = 0;
  41        /* i_data is not going away, no lock needed */
  42        add_chain (chain, NULL, i_data(inode) + *offsets);
  43        if (!p->key)
  44                goto no_block;
  45        while (--depth) {
  46                bh = sb_bread(sb, block_to_cpu(p->key));
  47                if (!bh)
  48                        goto failure;
  49                read_lock(&pointers_lock);
  50                if (!verify_chain(chain, p))
  51                        goto changed;
  52                add_chain(++p, bh, (block_t *)bh->b_data + *++offsets);
  53                read_unlock(&pointers_lock);
  54                if (!p->key)
  55                        goto no_block;
  56        }
  57        return NULL;
  58
  59changed:
  60        read_unlock(&pointers_lock);
  61        brelse(bh);
  62        *err = -EAGAIN;
  63        goto no_block;
  64failure:
  65        *err = -EIO;
  66no_block:
  67        return p;
  68}
  69
  70static int alloc_branch(struct inode *inode,
  71                             int num,
  72                             int *offsets,
  73                             Indirect *branch)
  74{
  75        int n = 0;
  76        int i;
  77        int parent = minix_new_block(inode);
  78        int err = -ENOSPC;
  79
  80        branch[0].key = cpu_to_block(parent);
  81        if (parent) for (n = 1; n < num; n++) {
  82                struct buffer_head *bh;
  83                /* Allocate the next block */
  84                int nr = minix_new_block(inode);
  85                if (!nr)
  86                        break;
  87                branch[n].key = cpu_to_block(nr);
  88                bh = sb_getblk(inode->i_sb, parent);
  89                if (!bh) {
  90                        minix_free_block(inode, nr);
  91                        err = -ENOMEM;
  92                        break;
  93                }
  94                lock_buffer(bh);
  95                memset(bh->b_data, 0, bh->b_size);
  96                branch[n].bh = bh;
  97                branch[n].p = (block_t*) bh->b_data + offsets[n];
  98                *branch[n].p = branch[n].key;
  99                set_buffer_uptodate(bh);
 100                unlock_buffer(bh);
 101                mark_buffer_dirty_inode(bh, inode);
 102                parent = nr;
 103        }
 104        if (n == num)
 105                return 0;
 106
 107        /* Allocation failed, free what we already allocated */
 108        for (i = 1; i < n; i++)
 109                bforget(branch[i].bh);
 110        for (i = 0; i < n; i++)
 111                minix_free_block(inode, block_to_cpu(branch[i].key));
 112        return err;
 113}
 114
 115static inline int splice_branch(struct inode *inode,
 116                                     Indirect chain[DEPTH],
 117                                     Indirect *where,
 118                                     int num)
 119{
 120        int i;
 121
 122        write_lock(&pointers_lock);
 123
 124        /* Verify that place we are splicing to is still there and vacant */
 125        if (!verify_chain(chain, where-1) || *where->p)
 126                goto changed;
 127
 128        *where->p = where->key;
 129
 130        write_unlock(&pointers_lock);
 131
 132        /* We are done with atomic stuff, now do the rest of housekeeping */
 133
 134        inode->i_ctime = current_time(inode);
 135
 136        /* had we spliced it onto indirect block? */
 137        if (where->bh)
 138                mark_buffer_dirty_inode(where->bh, inode);
 139
 140        mark_inode_dirty(inode);
 141        return 0;
 142
 143changed:
 144        write_unlock(&pointers_lock);
 145        for (i = 1; i < num; i++)
 146                bforget(where[i].bh);
 147        for (i = 0; i < num; i++)
 148                minix_free_block(inode, block_to_cpu(where[i].key));
 149        return -EAGAIN;
 150}
 151
 152static int get_block(struct inode * inode, sector_t block,
 153                        struct buffer_head *bh, int create)
 154{
 155        int err = -EIO;
 156        int offsets[DEPTH];
 157        Indirect chain[DEPTH];
 158        Indirect *partial;
 159        int left;
 160        int depth = block_to_path(inode, block, offsets);
 161
 162        if (depth == 0)
 163                goto out;
 164
 165reread:
 166        partial = get_branch(inode, depth, offsets, chain, &err);
 167
 168        /* Simplest case - block found, no allocation needed */
 169        if (!partial) {
 170got_it:
 171                map_bh(bh, inode->i_sb, block_to_cpu(chain[depth-1].key));
 172                /* Clean up and exit */
 173                partial = chain+depth-1; /* the whole chain */
 174                goto cleanup;
 175        }
 176
 177        /* Next simple case - plain lookup or failed read of indirect block */
 178        if (!create || err == -EIO) {
 179cleanup:
 180                while (partial > chain) {
 181                        brelse(partial->bh);
 182                        partial--;
 183                }
 184out:
 185                return err;
 186        }
 187
 188        /*
 189         * Indirect block might be removed by truncate while we were
 190         * reading it. Handling of that case (forget what we've got and
 191         * reread) is taken out of the main path.
 192         */
 193        if (err == -EAGAIN)
 194                goto changed;
 195
 196        left = (chain + depth) - partial;
 197        err = alloc_branch(inode, left, offsets+(partial-chain), partial);
 198        if (err)
 199                goto cleanup;
 200
 201        if (splice_branch(inode, chain, partial, left) < 0)
 202                goto changed;
 203
 204        set_buffer_new(bh);
 205        goto got_it;
 206
 207changed:
 208        while (partial > chain) {
 209                brelse(partial->bh);
 210                partial--;
 211        }
 212        goto reread;
 213}
 214
 215static inline int all_zeroes(block_t *p, block_t *q)
 216{
 217        while (p < q)
 218                if (*p++)
 219                        return 0;
 220        return 1;
 221}
 222
 223static Indirect *find_shared(struct inode *inode,
 224                                int depth,
 225                                int offsets[DEPTH],
 226                                Indirect chain[DEPTH],
 227                                block_t *top)
 228{
 229        Indirect *partial, *p;
 230        int k, err;
 231
 232        *top = 0;
 233        for (k = depth; k > 1 && !offsets[k-1]; k--)
 234                ;
 235        partial = get_branch(inode, k, offsets, chain, &err);
 236
 237        write_lock(&pointers_lock);
 238        if (!partial)
 239                partial = chain + k-1;
 240        if (!partial->key && *partial->p) {
 241                write_unlock(&pointers_lock);
 242                goto no_top;
 243        }
 244        for (p=partial;p>chain && all_zeroes((block_t*)p->bh->b_data,p->p);p--)
 245                ;
 246        if (p == chain + k - 1 && p > chain) {
 247                p->p--;
 248        } else {
 249                *top = *p->p;
 250                *p->p = 0;
 251        }
 252        write_unlock(&pointers_lock);
 253
 254        while(partial > p)
 255        {
 256                brelse(partial->bh);
 257                partial--;
 258        }
 259no_top:
 260        return partial;
 261}
 262
 263static inline void free_data(struct inode *inode, block_t *p, block_t *q)
 264{
 265        unsigned long nr;
 266
 267        for ( ; p < q ; p++) {
 268                nr = block_to_cpu(*p);
 269                if (nr) {
 270                        *p = 0;
 271                        minix_free_block(inode, nr);
 272                }
 273        }
 274}
 275
 276static void free_branches(struct inode *inode, block_t *p, block_t *q, int depth)
 277{
 278        struct buffer_head * bh;
 279        unsigned long nr;
 280
 281        if (depth--) {
 282                for ( ; p < q ; p++) {
 283                        nr = block_to_cpu(*p);
 284                        if (!nr)
 285                                continue;
 286                        *p = 0;
 287                        bh = sb_bread(inode->i_sb, nr);
 288                        if (!bh)
 289                                continue;
 290                        free_branches(inode, (block_t*)bh->b_data,
 291                                      block_end(bh), depth);
 292                        bforget(bh);
 293                        minix_free_block(inode, nr);
 294                        mark_inode_dirty(inode);
 295                }
 296        } else
 297                free_data(inode, p, q);
 298}
 299
 300static inline void truncate (struct inode * inode)
 301{
 302        struct super_block *sb = inode->i_sb;
 303        block_t *idata = i_data(inode);
 304        int offsets[DEPTH];
 305        Indirect chain[DEPTH];
 306        Indirect *partial;
 307        block_t nr = 0;
 308        int n;
 309        int first_whole;
 310        long iblock;
 311
 312        iblock = (inode->i_size + sb->s_blocksize -1) >> sb->s_blocksize_bits;
 313        block_truncate_page(inode->i_mapping, inode->i_size, get_block);
 314
 315        n = block_to_path(inode, iblock, offsets);
 316        if (!n)
 317                return;
 318
 319        if (n == 1) {
 320                free_data(inode, idata+offsets[0], idata + DIRECT);
 321                first_whole = 0;
 322                goto do_indirects;
 323        }
 324
 325        first_whole = offsets[0] + 1 - DIRECT;
 326        partial = find_shared(inode, n, offsets, chain, &nr);
 327        if (nr) {
 328                if (partial == chain)
 329                        mark_inode_dirty(inode);
 330                else
 331                        mark_buffer_dirty_inode(partial->bh, inode);
 332                free_branches(inode, &nr, &nr+1, (chain+n-1) - partial);
 333        }
 334        /* Clear the ends of indirect blocks on the shared branch */
 335        while (partial > chain) {
 336                free_branches(inode, partial->p + 1, block_end(partial->bh),
 337                                (chain+n-1) - partial);
 338                mark_buffer_dirty_inode(partial->bh, inode);
 339                brelse (partial->bh);
 340                partial--;
 341        }
 342do_indirects:
 343        /* Kill the remaining (whole) subtrees */
 344        while (first_whole < DEPTH-1) {
 345                nr = idata[DIRECT+first_whole];
 346                if (nr) {
 347                        idata[DIRECT+first_whole] = 0;
 348                        mark_inode_dirty(inode);
 349                        free_branches(inode, &nr, &nr+1, first_whole+1);
 350                }
 351                first_whole++;
 352        }
 353        inode->i_mtime = inode->i_ctime = current_time(inode);
 354        mark_inode_dirty(inode);
 355}
 356
 357static inline unsigned nblocks(loff_t size, struct super_block *sb)
 358{
 359        int k = sb->s_blocksize_bits - 10;
 360        unsigned blocks, res, direct = DIRECT, i = DEPTH;
 361        blocks = (size + sb->s_blocksize - 1) >> (BLOCK_SIZE_BITS + k);
 362        res = blocks;
 363        while (--i && blocks > direct) {
 364                blocks -= direct;
 365                blocks += sb->s_blocksize/sizeof(block_t) - 1;
 366                blocks /= sb->s_blocksize/sizeof(block_t);
 367                res += blocks;
 368                direct = 1;
 369        }
 370        return res;
 371}
 372