linux/fs/afs/dir_edit.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/* AFS filesystem directory editing
   3 *
   4 * Copyright (C) 2018 Red Hat, Inc. All Rights Reserved.
   5 * Written by David Howells (dhowells@redhat.com)
   6 */
   7
   8#include <linux/kernel.h>
   9#include <linux/fs.h>
  10#include <linux/namei.h>
  11#include <linux/pagemap.h>
  12#include <linux/iversion.h>
  13#include "internal.h"
  14#include "xdr_fs.h"
  15
  16/*
  17 * Find a number of contiguous clear bits in a directory block bitmask.
  18 *
  19 * There are 64 slots, which means we can load the entire bitmap into a
  20 * variable.  The first bit doesn't count as it corresponds to the block header
  21 * slot.  nr_slots is between 1 and 9.
  22 */
  23static int afs_find_contig_bits(union afs_xdr_dir_block *block, unsigned int nr_slots)
  24{
  25        u64 bitmap;
  26        u32 mask;
  27        int bit, n;
  28
  29        bitmap  = (u64)block->hdr.bitmap[0] << 0 * 8;
  30        bitmap |= (u64)block->hdr.bitmap[1] << 1 * 8;
  31        bitmap |= (u64)block->hdr.bitmap[2] << 2 * 8;
  32        bitmap |= (u64)block->hdr.bitmap[3] << 3 * 8;
  33        bitmap |= (u64)block->hdr.bitmap[4] << 4 * 8;
  34        bitmap |= (u64)block->hdr.bitmap[5] << 5 * 8;
  35        bitmap |= (u64)block->hdr.bitmap[6] << 6 * 8;
  36        bitmap |= (u64)block->hdr.bitmap[7] << 7 * 8;
  37        bitmap >>= 1; /* The first entry is metadata */
  38        bit = 1;
  39        mask = (1 << nr_slots) - 1;
  40
  41        do {
  42                if (sizeof(unsigned long) == 8)
  43                        n = ffz(bitmap);
  44                else
  45                        n = ((u32)bitmap) != 0 ?
  46                                ffz((u32)bitmap) :
  47                                ffz((u32)(bitmap >> 32)) + 32;
  48                bitmap >>= n;
  49                bit += n;
  50
  51                if ((bitmap & mask) == 0) {
  52                        if (bit > 64 - nr_slots)
  53                                return -1;
  54                        return bit;
  55                }
  56
  57                n = __ffs(bitmap);
  58                bitmap >>= n;
  59                bit += n;
  60        } while (bitmap);
  61
  62        return -1;
  63}
  64
  65/*
  66 * Set a number of contiguous bits in the directory block bitmap.
  67 */
  68static void afs_set_contig_bits(union afs_xdr_dir_block *block,
  69                                int bit, unsigned int nr_slots)
  70{
  71        u64 mask;
  72
  73        mask = (1 << nr_slots) - 1;
  74        mask <<= bit;
  75
  76        block->hdr.bitmap[0] |= (u8)(mask >> 0 * 8);
  77        block->hdr.bitmap[1] |= (u8)(mask >> 1 * 8);
  78        block->hdr.bitmap[2] |= (u8)(mask >> 2 * 8);
  79        block->hdr.bitmap[3] |= (u8)(mask >> 3 * 8);
  80        block->hdr.bitmap[4] |= (u8)(mask >> 4 * 8);
  81        block->hdr.bitmap[5] |= (u8)(mask >> 5 * 8);
  82        block->hdr.bitmap[6] |= (u8)(mask >> 6 * 8);
  83        block->hdr.bitmap[7] |= (u8)(mask >> 7 * 8);
  84}
  85
  86/*
  87 * Clear a number of contiguous bits in the directory block bitmap.
  88 */
  89static void afs_clear_contig_bits(union afs_xdr_dir_block *block,
  90                                  int bit, unsigned int nr_slots)
  91{
  92        u64 mask;
  93
  94        mask = (1 << nr_slots) - 1;
  95        mask <<= bit;
  96
  97        block->hdr.bitmap[0] &= ~(u8)(mask >> 0 * 8);
  98        block->hdr.bitmap[1] &= ~(u8)(mask >> 1 * 8);
  99        block->hdr.bitmap[2] &= ~(u8)(mask >> 2 * 8);
 100        block->hdr.bitmap[3] &= ~(u8)(mask >> 3 * 8);
 101        block->hdr.bitmap[4] &= ~(u8)(mask >> 4 * 8);
 102        block->hdr.bitmap[5] &= ~(u8)(mask >> 5 * 8);
 103        block->hdr.bitmap[6] &= ~(u8)(mask >> 6 * 8);
 104        block->hdr.bitmap[7] &= ~(u8)(mask >> 7 * 8);
 105}
 106
 107/*
 108 * Scan a directory block looking for a dirent of the right name.
 109 */
 110static int afs_dir_scan_block(union afs_xdr_dir_block *block, struct qstr *name,
 111                              unsigned int blocknum)
 112{
 113        union afs_xdr_dirent *de;
 114        u64 bitmap;
 115        int d, len, n;
 116
 117        _enter("");
 118
 119        bitmap  = (u64)block->hdr.bitmap[0] << 0 * 8;
 120        bitmap |= (u64)block->hdr.bitmap[1] << 1 * 8;
 121        bitmap |= (u64)block->hdr.bitmap[2] << 2 * 8;
 122        bitmap |= (u64)block->hdr.bitmap[3] << 3 * 8;
 123        bitmap |= (u64)block->hdr.bitmap[4] << 4 * 8;
 124        bitmap |= (u64)block->hdr.bitmap[5] << 5 * 8;
 125        bitmap |= (u64)block->hdr.bitmap[6] << 6 * 8;
 126        bitmap |= (u64)block->hdr.bitmap[7] << 7 * 8;
 127
 128        for (d = (blocknum == 0 ? AFS_DIR_RESV_BLOCKS0 : AFS_DIR_RESV_BLOCKS);
 129             d < AFS_DIR_SLOTS_PER_BLOCK;
 130             d++) {
 131                if (!((bitmap >> d) & 1))
 132                        continue;
 133                de = &block->dirents[d];
 134                if (de->u.valid != 1)
 135                        continue;
 136
 137                /* The block was NUL-terminated by afs_dir_check_page(). */
 138                len = strlen(de->u.name);
 139                if (len == name->len &&
 140                    memcmp(de->u.name, name->name, name->len) == 0)
 141                        return d;
 142
 143                n = round_up(12 + len + 1 + 4, AFS_DIR_DIRENT_SIZE);
 144                n /= AFS_DIR_DIRENT_SIZE;
 145                d += n - 1;
 146        }
 147
 148        return -1;
 149}
 150
 151/*
 152 * Initialise a new directory block.  Note that block 0 is special and contains
 153 * some extra metadata.
 154 */
 155static void afs_edit_init_block(union afs_xdr_dir_block *meta,
 156                                union afs_xdr_dir_block *block, int block_num)
 157{
 158        memset(block, 0, sizeof(*block));
 159        block->hdr.npages = htons(1);
 160        block->hdr.magic = AFS_DIR_MAGIC;
 161        block->hdr.bitmap[0] = 1;
 162
 163        if (block_num == 0) {
 164                block->hdr.bitmap[0] = 0xff;
 165                block->hdr.bitmap[1] = 0x1f;
 166                memset(block->meta.alloc_ctrs,
 167                       AFS_DIR_SLOTS_PER_BLOCK,
 168                       sizeof(block->meta.alloc_ctrs));
 169                meta->meta.alloc_ctrs[0] =
 170                        AFS_DIR_SLOTS_PER_BLOCK - AFS_DIR_RESV_BLOCKS0;
 171        }
 172
 173        if (block_num < AFS_DIR_BLOCKS_WITH_CTR)
 174                meta->meta.alloc_ctrs[block_num] =
 175                        AFS_DIR_SLOTS_PER_BLOCK - AFS_DIR_RESV_BLOCKS;
 176}
 177
 178/*
 179 * Edit a directory's file data to add a new directory entry.  Doing this after
 180 * create, mkdir, symlink, link or rename if the data version number is
 181 * incremented by exactly one avoids the need to re-download the entire
 182 * directory contents.
 183 *
 184 * The caller must hold the inode locked.
 185 */
 186void afs_edit_dir_add(struct afs_vnode *vnode,
 187                      struct qstr *name, struct afs_fid *new_fid,
 188                      enum afs_edit_dir_reason why)
 189{
 190        union afs_xdr_dir_block *meta, *block;
 191        struct afs_xdr_dir_page *meta_page, *dir_page;
 192        union afs_xdr_dirent *de;
 193        struct page *page0, *page;
 194        unsigned int need_slots, nr_blocks, b;
 195        pgoff_t index;
 196        loff_t i_size;
 197        gfp_t gfp;
 198        int slot;
 199
 200        _enter(",,{%d,%s},", name->len, name->name);
 201
 202        i_size = i_size_read(&vnode->vfs_inode);
 203        if (i_size > AFS_DIR_BLOCK_SIZE * AFS_DIR_MAX_BLOCKS ||
 204            (i_size & (AFS_DIR_BLOCK_SIZE - 1))) {
 205                clear_bit(AFS_VNODE_DIR_VALID, &vnode->flags);
 206                return;
 207        }
 208
 209        gfp = vnode->vfs_inode.i_mapping->gfp_mask;
 210        page0 = find_or_create_page(vnode->vfs_inode.i_mapping, 0, gfp);
 211        if (!page0) {
 212                clear_bit(AFS_VNODE_DIR_VALID, &vnode->flags);
 213                _leave(" [fgp]");
 214                return;
 215        }
 216
 217        /* Work out how many slots we're going to need. */
 218        need_slots = afs_dir_calc_slots(name->len);
 219
 220        meta_page = kmap(page0);
 221        meta = &meta_page->blocks[0];
 222        if (i_size == 0)
 223                goto new_directory;
 224        nr_blocks = i_size / AFS_DIR_BLOCK_SIZE;
 225
 226        /* Find a block that has sufficient slots available.  Each VM page
 227         * contains two or more directory blocks.
 228         */
 229        for (b = 0; b < nr_blocks + 1; b++) {
 230                /* If the directory extended into a new page, then we need to
 231                 * tack a new page on the end.
 232                 */
 233                index = b / AFS_DIR_BLOCKS_PER_PAGE;
 234                if (index == 0) {
 235                        page = page0;
 236                        dir_page = meta_page;
 237                } else {
 238                        if (nr_blocks >= AFS_DIR_MAX_BLOCKS)
 239                                goto error;
 240                        gfp = vnode->vfs_inode.i_mapping->gfp_mask;
 241                        page = find_or_create_page(vnode->vfs_inode.i_mapping,
 242                                                   index, gfp);
 243                        if (!page)
 244                                goto error;
 245                        if (!PagePrivate(page))
 246                                attach_page_private(page, (void *)1);
 247                        dir_page = kmap(page);
 248                }
 249
 250                /* Abandon the edit if we got a callback break. */
 251                if (!test_bit(AFS_VNODE_DIR_VALID, &vnode->flags))
 252                        goto invalidated;
 253
 254                block = &dir_page->blocks[b % AFS_DIR_BLOCKS_PER_PAGE];
 255
 256                _debug("block %u: %2u %3u %u",
 257                       b,
 258                       (b < AFS_DIR_BLOCKS_WITH_CTR) ? meta->meta.alloc_ctrs[b] : 99,
 259                       ntohs(block->hdr.npages),
 260                       ntohs(block->hdr.magic));
 261
 262                /* Initialise the block if necessary. */
 263                if (b == nr_blocks) {
 264                        _debug("init %u", b);
 265                        afs_edit_init_block(meta, block, b);
 266                        afs_set_i_size(vnode, (b + 1) * AFS_DIR_BLOCK_SIZE);
 267                }
 268
 269                /* Only lower dir pages have a counter in the header. */
 270                if (b >= AFS_DIR_BLOCKS_WITH_CTR ||
 271                    meta->meta.alloc_ctrs[b] >= need_slots) {
 272                        /* We need to try and find one or more consecutive
 273                         * slots to hold the entry.
 274                         */
 275                        slot = afs_find_contig_bits(block, need_slots);
 276                        if (slot >= 0) {
 277                                _debug("slot %u", slot);
 278                                goto found_space;
 279                        }
 280                }
 281
 282                if (page != page0) {
 283                        unlock_page(page);
 284                        kunmap(page);
 285                        put_page(page);
 286                }
 287        }
 288
 289        /* There are no spare slots of sufficient size, yet the operation
 290         * succeeded.  Download the directory again.
 291         */
 292        trace_afs_edit_dir(vnode, why, afs_edit_dir_create_nospc, 0, 0, 0, 0, name->name);
 293        clear_bit(AFS_VNODE_DIR_VALID, &vnode->flags);
 294        goto out_unmap;
 295
 296new_directory:
 297        afs_edit_init_block(meta, meta, 0);
 298        i_size = AFS_DIR_BLOCK_SIZE;
 299        afs_set_i_size(vnode, i_size);
 300        slot = AFS_DIR_RESV_BLOCKS0;
 301        page = page0;
 302        block = meta;
 303        nr_blocks = 1;
 304        b = 0;
 305
 306found_space:
 307        /* Set the dirent slot. */
 308        trace_afs_edit_dir(vnode, why, afs_edit_dir_create, b, slot,
 309                           new_fid->vnode, new_fid->unique, name->name);
 310        de = &block->dirents[slot];
 311        de->u.valid     = 1;
 312        de->u.unused[0] = 0;
 313        de->u.hash_next = 0; // TODO: Really need to maintain this
 314        de->u.vnode     = htonl(new_fid->vnode);
 315        de->u.unique    = htonl(new_fid->unique);
 316        memcpy(de->u.name, name->name, name->len + 1);
 317        de->u.name[name->len] = 0;
 318
 319        /* Adjust the bitmap. */
 320        afs_set_contig_bits(block, slot, need_slots);
 321        if (page != page0) {
 322                unlock_page(page);
 323                kunmap(page);
 324                put_page(page);
 325        }
 326
 327        /* Adjust the allocation counter. */
 328        if (b < AFS_DIR_BLOCKS_WITH_CTR)
 329                meta->meta.alloc_ctrs[b] -= need_slots;
 330
 331        inode_inc_iversion_raw(&vnode->vfs_inode);
 332        afs_stat_v(vnode, n_dir_cr);
 333        _debug("Insert %s in %u[%u]", name->name, b, slot);
 334
 335out_unmap:
 336        unlock_page(page0);
 337        kunmap(page0);
 338        put_page(page0);
 339        _leave("");
 340        return;
 341
 342invalidated:
 343        trace_afs_edit_dir(vnode, why, afs_edit_dir_create_inval, 0, 0, 0, 0, name->name);
 344        clear_bit(AFS_VNODE_DIR_VALID, &vnode->flags);
 345        if (page != page0) {
 346                kunmap(page);
 347                put_page(page);
 348        }
 349        goto out_unmap;
 350
 351error:
 352        trace_afs_edit_dir(vnode, why, afs_edit_dir_create_error, 0, 0, 0, 0, name->name);
 353        clear_bit(AFS_VNODE_DIR_VALID, &vnode->flags);
 354        goto out_unmap;
 355}
 356
 357/*
 358 * Edit a directory's file data to remove a new directory entry.  Doing this
 359 * after unlink, rmdir or rename if the data version number is incremented by
 360 * exactly one avoids the need to re-download the entire directory contents.
 361 *
 362 * The caller must hold the inode locked.
 363 */
 364void afs_edit_dir_remove(struct afs_vnode *vnode,
 365                         struct qstr *name, enum afs_edit_dir_reason why)
 366{
 367        struct afs_xdr_dir_page *meta_page, *dir_page;
 368        union afs_xdr_dir_block *meta, *block;
 369        union afs_xdr_dirent *de;
 370        struct page *page0, *page;
 371        unsigned int need_slots, nr_blocks, b;
 372        pgoff_t index;
 373        loff_t i_size;
 374        int slot;
 375
 376        _enter(",,{%d,%s},", name->len, name->name);
 377
 378        i_size = i_size_read(&vnode->vfs_inode);
 379        if (i_size < AFS_DIR_BLOCK_SIZE ||
 380            i_size > AFS_DIR_BLOCK_SIZE * AFS_DIR_MAX_BLOCKS ||
 381            (i_size & (AFS_DIR_BLOCK_SIZE - 1))) {
 382                clear_bit(AFS_VNODE_DIR_VALID, &vnode->flags);
 383                return;
 384        }
 385        nr_blocks = i_size / AFS_DIR_BLOCK_SIZE;
 386
 387        page0 = find_lock_page(vnode->vfs_inode.i_mapping, 0);
 388        if (!page0) {
 389                clear_bit(AFS_VNODE_DIR_VALID, &vnode->flags);
 390                _leave(" [fgp]");
 391                return;
 392        }
 393
 394        /* Work out how many slots we're going to discard. */
 395        need_slots = afs_dir_calc_slots(name->len);
 396
 397        meta_page = kmap(page0);
 398        meta = &meta_page->blocks[0];
 399
 400        /* Find a page that has sufficient slots available.  Each VM page
 401         * contains two or more directory blocks.
 402         */
 403        for (b = 0; b < nr_blocks; b++) {
 404                index = b / AFS_DIR_BLOCKS_PER_PAGE;
 405                if (index != 0) {
 406                        page = find_lock_page(vnode->vfs_inode.i_mapping, index);
 407                        if (!page)
 408                                goto error;
 409                        dir_page = kmap(page);
 410                } else {
 411                        page = page0;
 412                        dir_page = meta_page;
 413                }
 414
 415                /* Abandon the edit if we got a callback break. */
 416                if (!test_bit(AFS_VNODE_DIR_VALID, &vnode->flags))
 417                        goto invalidated;
 418
 419                block = &dir_page->blocks[b % AFS_DIR_BLOCKS_PER_PAGE];
 420
 421                if (b > AFS_DIR_BLOCKS_WITH_CTR ||
 422                    meta->meta.alloc_ctrs[b] <= AFS_DIR_SLOTS_PER_BLOCK - 1 - need_slots) {
 423                        slot = afs_dir_scan_block(block, name, b);
 424                        if (slot >= 0)
 425                                goto found_dirent;
 426                }
 427
 428                if (page != page0) {
 429                        unlock_page(page);
 430                        kunmap(page);
 431                        put_page(page);
 432                }
 433        }
 434
 435        /* Didn't find the dirent to clobber.  Download the directory again. */
 436        trace_afs_edit_dir(vnode, why, afs_edit_dir_delete_noent,
 437                           0, 0, 0, 0, name->name);
 438        clear_bit(AFS_VNODE_DIR_VALID, &vnode->flags);
 439        goto out_unmap;
 440
 441found_dirent:
 442        de = &block->dirents[slot];
 443
 444        trace_afs_edit_dir(vnode, why, afs_edit_dir_delete, b, slot,
 445                           ntohl(de->u.vnode), ntohl(de->u.unique),
 446                           name->name);
 447
 448        memset(de, 0, sizeof(*de) * need_slots);
 449
 450        /* Adjust the bitmap. */
 451        afs_clear_contig_bits(block, slot, need_slots);
 452        if (page != page0) {
 453                unlock_page(page);
 454                kunmap(page);
 455                put_page(page);
 456        }
 457
 458        /* Adjust the allocation counter. */
 459        if (b < AFS_DIR_BLOCKS_WITH_CTR)
 460                meta->meta.alloc_ctrs[b] += need_slots;
 461
 462        inode_set_iversion_raw(&vnode->vfs_inode, vnode->status.data_version);
 463        afs_stat_v(vnode, n_dir_rm);
 464        _debug("Remove %s from %u[%u]", name->name, b, slot);
 465
 466out_unmap:
 467        unlock_page(page0);
 468        kunmap(page0);
 469        put_page(page0);
 470        _leave("");
 471        return;
 472
 473invalidated:
 474        trace_afs_edit_dir(vnode, why, afs_edit_dir_delete_inval,
 475                           0, 0, 0, 0, name->name);
 476        clear_bit(AFS_VNODE_DIR_VALID, &vnode->flags);
 477        if (page != page0) {
 478                unlock_page(page);
 479                kunmap(page);
 480                put_page(page);
 481        }
 482        goto out_unmap;
 483
 484error:
 485        trace_afs_edit_dir(vnode, why, afs_edit_dir_delete_error,
 486                           0, 0, 0, 0, name->name);
 487        clear_bit(AFS_VNODE_DIR_VALID, &vnode->flags);
 488        goto out_unmap;
 489}
 490