162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* AFS filesystem directory editing 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (C) 2018 Red Hat, Inc. All Rights Reserved. 562306a36Sopenharmony_ci * Written by David Howells (dhowells@redhat.com) 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/kernel.h> 962306a36Sopenharmony_ci#include <linux/fs.h> 1062306a36Sopenharmony_ci#include <linux/namei.h> 1162306a36Sopenharmony_ci#include <linux/pagemap.h> 1262306a36Sopenharmony_ci#include <linux/iversion.h> 1362306a36Sopenharmony_ci#include "internal.h" 1462306a36Sopenharmony_ci#include "xdr_fs.h" 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci/* 1762306a36Sopenharmony_ci * Find a number of contiguous clear bits in a directory block bitmask. 1862306a36Sopenharmony_ci * 1962306a36Sopenharmony_ci * There are 64 slots, which means we can load the entire bitmap into a 2062306a36Sopenharmony_ci * variable. The first bit doesn't count as it corresponds to the block header 2162306a36Sopenharmony_ci * slot. nr_slots is between 1 and 9. 2262306a36Sopenharmony_ci */ 2362306a36Sopenharmony_cistatic int afs_find_contig_bits(union afs_xdr_dir_block *block, unsigned int nr_slots) 2462306a36Sopenharmony_ci{ 2562306a36Sopenharmony_ci u64 bitmap; 2662306a36Sopenharmony_ci u32 mask; 2762306a36Sopenharmony_ci int bit, n; 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci bitmap = (u64)block->hdr.bitmap[0] << 0 * 8; 3062306a36Sopenharmony_ci bitmap |= (u64)block->hdr.bitmap[1] << 1 * 8; 3162306a36Sopenharmony_ci bitmap |= (u64)block->hdr.bitmap[2] << 2 * 8; 3262306a36Sopenharmony_ci bitmap |= (u64)block->hdr.bitmap[3] << 3 * 8; 3362306a36Sopenharmony_ci bitmap |= (u64)block->hdr.bitmap[4] << 4 * 8; 3462306a36Sopenharmony_ci bitmap |= (u64)block->hdr.bitmap[5] << 5 * 8; 3562306a36Sopenharmony_ci bitmap |= (u64)block->hdr.bitmap[6] << 6 * 8; 3662306a36Sopenharmony_ci bitmap |= (u64)block->hdr.bitmap[7] << 7 * 8; 3762306a36Sopenharmony_ci bitmap >>= 1; /* The first entry is metadata */ 3862306a36Sopenharmony_ci bit = 1; 3962306a36Sopenharmony_ci mask = (1 << nr_slots) - 1; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci do { 4262306a36Sopenharmony_ci if (sizeof(unsigned long) == 8) 4362306a36Sopenharmony_ci n = ffz(bitmap); 4462306a36Sopenharmony_ci else 4562306a36Sopenharmony_ci n = ((u32)bitmap) != 0 ? 4662306a36Sopenharmony_ci ffz((u32)bitmap) : 4762306a36Sopenharmony_ci ffz((u32)(bitmap >> 32)) + 32; 4862306a36Sopenharmony_ci bitmap >>= n; 4962306a36Sopenharmony_ci bit += n; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci if ((bitmap & mask) == 0) { 5262306a36Sopenharmony_ci if (bit > 64 - nr_slots) 5362306a36Sopenharmony_ci return -1; 5462306a36Sopenharmony_ci return bit; 5562306a36Sopenharmony_ci } 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci n = __ffs(bitmap); 5862306a36Sopenharmony_ci bitmap >>= n; 5962306a36Sopenharmony_ci bit += n; 6062306a36Sopenharmony_ci } while (bitmap); 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci return -1; 6362306a36Sopenharmony_ci} 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci/* 6662306a36Sopenharmony_ci * Set a number of contiguous bits in the directory block bitmap. 6762306a36Sopenharmony_ci */ 6862306a36Sopenharmony_cistatic void afs_set_contig_bits(union afs_xdr_dir_block *block, 6962306a36Sopenharmony_ci int bit, unsigned int nr_slots) 7062306a36Sopenharmony_ci{ 7162306a36Sopenharmony_ci u64 mask; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci mask = (1 << nr_slots) - 1; 7462306a36Sopenharmony_ci mask <<= bit; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci block->hdr.bitmap[0] |= (u8)(mask >> 0 * 8); 7762306a36Sopenharmony_ci block->hdr.bitmap[1] |= (u8)(mask >> 1 * 8); 7862306a36Sopenharmony_ci block->hdr.bitmap[2] |= (u8)(mask >> 2 * 8); 7962306a36Sopenharmony_ci block->hdr.bitmap[3] |= (u8)(mask >> 3 * 8); 8062306a36Sopenharmony_ci block->hdr.bitmap[4] |= (u8)(mask >> 4 * 8); 8162306a36Sopenharmony_ci block->hdr.bitmap[5] |= (u8)(mask >> 5 * 8); 8262306a36Sopenharmony_ci block->hdr.bitmap[6] |= (u8)(mask >> 6 * 8); 8362306a36Sopenharmony_ci block->hdr.bitmap[7] |= (u8)(mask >> 7 * 8); 8462306a36Sopenharmony_ci} 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci/* 8762306a36Sopenharmony_ci * Clear a number of contiguous bits in the directory block bitmap. 8862306a36Sopenharmony_ci */ 8962306a36Sopenharmony_cistatic void afs_clear_contig_bits(union afs_xdr_dir_block *block, 9062306a36Sopenharmony_ci int bit, unsigned int nr_slots) 9162306a36Sopenharmony_ci{ 9262306a36Sopenharmony_ci u64 mask; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci mask = (1 << nr_slots) - 1; 9562306a36Sopenharmony_ci mask <<= bit; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci block->hdr.bitmap[0] &= ~(u8)(mask >> 0 * 8); 9862306a36Sopenharmony_ci block->hdr.bitmap[1] &= ~(u8)(mask >> 1 * 8); 9962306a36Sopenharmony_ci block->hdr.bitmap[2] &= ~(u8)(mask >> 2 * 8); 10062306a36Sopenharmony_ci block->hdr.bitmap[3] &= ~(u8)(mask >> 3 * 8); 10162306a36Sopenharmony_ci block->hdr.bitmap[4] &= ~(u8)(mask >> 4 * 8); 10262306a36Sopenharmony_ci block->hdr.bitmap[5] &= ~(u8)(mask >> 5 * 8); 10362306a36Sopenharmony_ci block->hdr.bitmap[6] &= ~(u8)(mask >> 6 * 8); 10462306a36Sopenharmony_ci block->hdr.bitmap[7] &= ~(u8)(mask >> 7 * 8); 10562306a36Sopenharmony_ci} 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci/* 10862306a36Sopenharmony_ci * Get a new directory folio. 10962306a36Sopenharmony_ci */ 11062306a36Sopenharmony_cistatic struct folio *afs_dir_get_folio(struct afs_vnode *vnode, pgoff_t index) 11162306a36Sopenharmony_ci{ 11262306a36Sopenharmony_ci struct address_space *mapping = vnode->netfs.inode.i_mapping; 11362306a36Sopenharmony_ci struct folio *folio; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci folio = __filemap_get_folio(mapping, index, 11662306a36Sopenharmony_ci FGP_LOCK | FGP_ACCESSED | FGP_CREAT, 11762306a36Sopenharmony_ci mapping->gfp_mask); 11862306a36Sopenharmony_ci if (IS_ERR(folio)) { 11962306a36Sopenharmony_ci clear_bit(AFS_VNODE_DIR_VALID, &vnode->flags); 12062306a36Sopenharmony_ci return NULL; 12162306a36Sopenharmony_ci } 12262306a36Sopenharmony_ci if (!folio_test_private(folio)) 12362306a36Sopenharmony_ci folio_attach_private(folio, (void *)1); 12462306a36Sopenharmony_ci return folio; 12562306a36Sopenharmony_ci} 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci/* 12862306a36Sopenharmony_ci * Scan a directory block looking for a dirent of the right name. 12962306a36Sopenharmony_ci */ 13062306a36Sopenharmony_cistatic int afs_dir_scan_block(union afs_xdr_dir_block *block, struct qstr *name, 13162306a36Sopenharmony_ci unsigned int blocknum) 13262306a36Sopenharmony_ci{ 13362306a36Sopenharmony_ci union afs_xdr_dirent *de; 13462306a36Sopenharmony_ci u64 bitmap; 13562306a36Sopenharmony_ci int d, len, n; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci _enter(""); 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci bitmap = (u64)block->hdr.bitmap[0] << 0 * 8; 14062306a36Sopenharmony_ci bitmap |= (u64)block->hdr.bitmap[1] << 1 * 8; 14162306a36Sopenharmony_ci bitmap |= (u64)block->hdr.bitmap[2] << 2 * 8; 14262306a36Sopenharmony_ci bitmap |= (u64)block->hdr.bitmap[3] << 3 * 8; 14362306a36Sopenharmony_ci bitmap |= (u64)block->hdr.bitmap[4] << 4 * 8; 14462306a36Sopenharmony_ci bitmap |= (u64)block->hdr.bitmap[5] << 5 * 8; 14562306a36Sopenharmony_ci bitmap |= (u64)block->hdr.bitmap[6] << 6 * 8; 14662306a36Sopenharmony_ci bitmap |= (u64)block->hdr.bitmap[7] << 7 * 8; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci for (d = (blocknum == 0 ? AFS_DIR_RESV_BLOCKS0 : AFS_DIR_RESV_BLOCKS); 14962306a36Sopenharmony_ci d < AFS_DIR_SLOTS_PER_BLOCK; 15062306a36Sopenharmony_ci d++) { 15162306a36Sopenharmony_ci if (!((bitmap >> d) & 1)) 15262306a36Sopenharmony_ci continue; 15362306a36Sopenharmony_ci de = &block->dirents[d]; 15462306a36Sopenharmony_ci if (de->u.valid != 1) 15562306a36Sopenharmony_ci continue; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci /* The block was NUL-terminated by afs_dir_check_page(). */ 15862306a36Sopenharmony_ci len = strlen(de->u.name); 15962306a36Sopenharmony_ci if (len == name->len && 16062306a36Sopenharmony_ci memcmp(de->u.name, name->name, name->len) == 0) 16162306a36Sopenharmony_ci return d; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci n = round_up(12 + len + 1 + 4, AFS_DIR_DIRENT_SIZE); 16462306a36Sopenharmony_ci n /= AFS_DIR_DIRENT_SIZE; 16562306a36Sopenharmony_ci d += n - 1; 16662306a36Sopenharmony_ci } 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci return -1; 16962306a36Sopenharmony_ci} 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci/* 17262306a36Sopenharmony_ci * Initialise a new directory block. Note that block 0 is special and contains 17362306a36Sopenharmony_ci * some extra metadata. 17462306a36Sopenharmony_ci */ 17562306a36Sopenharmony_cistatic void afs_edit_init_block(union afs_xdr_dir_block *meta, 17662306a36Sopenharmony_ci union afs_xdr_dir_block *block, int block_num) 17762306a36Sopenharmony_ci{ 17862306a36Sopenharmony_ci memset(block, 0, sizeof(*block)); 17962306a36Sopenharmony_ci block->hdr.npages = htons(1); 18062306a36Sopenharmony_ci block->hdr.magic = AFS_DIR_MAGIC; 18162306a36Sopenharmony_ci block->hdr.bitmap[0] = 1; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci if (block_num == 0) { 18462306a36Sopenharmony_ci block->hdr.bitmap[0] = 0xff; 18562306a36Sopenharmony_ci block->hdr.bitmap[1] = 0x1f; 18662306a36Sopenharmony_ci memset(block->meta.alloc_ctrs, 18762306a36Sopenharmony_ci AFS_DIR_SLOTS_PER_BLOCK, 18862306a36Sopenharmony_ci sizeof(block->meta.alloc_ctrs)); 18962306a36Sopenharmony_ci meta->meta.alloc_ctrs[0] = 19062306a36Sopenharmony_ci AFS_DIR_SLOTS_PER_BLOCK - AFS_DIR_RESV_BLOCKS0; 19162306a36Sopenharmony_ci } 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci if (block_num < AFS_DIR_BLOCKS_WITH_CTR) 19462306a36Sopenharmony_ci meta->meta.alloc_ctrs[block_num] = 19562306a36Sopenharmony_ci AFS_DIR_SLOTS_PER_BLOCK - AFS_DIR_RESV_BLOCKS; 19662306a36Sopenharmony_ci} 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci/* 19962306a36Sopenharmony_ci * Edit a directory's file data to add a new directory entry. Doing this after 20062306a36Sopenharmony_ci * create, mkdir, symlink, link or rename if the data version number is 20162306a36Sopenharmony_ci * incremented by exactly one avoids the need to re-download the entire 20262306a36Sopenharmony_ci * directory contents. 20362306a36Sopenharmony_ci * 20462306a36Sopenharmony_ci * The caller must hold the inode locked. 20562306a36Sopenharmony_ci */ 20662306a36Sopenharmony_civoid afs_edit_dir_add(struct afs_vnode *vnode, 20762306a36Sopenharmony_ci struct qstr *name, struct afs_fid *new_fid, 20862306a36Sopenharmony_ci enum afs_edit_dir_reason why) 20962306a36Sopenharmony_ci{ 21062306a36Sopenharmony_ci union afs_xdr_dir_block *meta, *block; 21162306a36Sopenharmony_ci union afs_xdr_dirent *de; 21262306a36Sopenharmony_ci struct folio *folio0, *folio; 21362306a36Sopenharmony_ci unsigned int need_slots, nr_blocks, b; 21462306a36Sopenharmony_ci pgoff_t index; 21562306a36Sopenharmony_ci loff_t i_size; 21662306a36Sopenharmony_ci int slot; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci _enter(",,{%d,%s},", name->len, name->name); 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci i_size = i_size_read(&vnode->netfs.inode); 22162306a36Sopenharmony_ci if (i_size > AFS_DIR_BLOCK_SIZE * AFS_DIR_MAX_BLOCKS || 22262306a36Sopenharmony_ci (i_size & (AFS_DIR_BLOCK_SIZE - 1))) { 22362306a36Sopenharmony_ci clear_bit(AFS_VNODE_DIR_VALID, &vnode->flags); 22462306a36Sopenharmony_ci return; 22562306a36Sopenharmony_ci } 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci folio0 = afs_dir_get_folio(vnode, 0); 22862306a36Sopenharmony_ci if (!folio0) { 22962306a36Sopenharmony_ci _leave(" [fgp]"); 23062306a36Sopenharmony_ci return; 23162306a36Sopenharmony_ci } 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci /* Work out how many slots we're going to need. */ 23462306a36Sopenharmony_ci need_slots = afs_dir_calc_slots(name->len); 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci meta = kmap_local_folio(folio0, 0); 23762306a36Sopenharmony_ci if (i_size == 0) 23862306a36Sopenharmony_ci goto new_directory; 23962306a36Sopenharmony_ci nr_blocks = i_size / AFS_DIR_BLOCK_SIZE; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci /* Find a block that has sufficient slots available. Each folio 24262306a36Sopenharmony_ci * contains two or more directory blocks. 24362306a36Sopenharmony_ci */ 24462306a36Sopenharmony_ci for (b = 0; b < nr_blocks + 1; b++) { 24562306a36Sopenharmony_ci /* If the directory extended into a new folio, then we need to 24662306a36Sopenharmony_ci * tack a new folio on the end. 24762306a36Sopenharmony_ci */ 24862306a36Sopenharmony_ci index = b / AFS_DIR_BLOCKS_PER_PAGE; 24962306a36Sopenharmony_ci if (nr_blocks >= AFS_DIR_MAX_BLOCKS) 25062306a36Sopenharmony_ci goto error; 25162306a36Sopenharmony_ci if (index >= folio_nr_pages(folio0)) { 25262306a36Sopenharmony_ci folio = afs_dir_get_folio(vnode, index); 25362306a36Sopenharmony_ci if (!folio) 25462306a36Sopenharmony_ci goto error; 25562306a36Sopenharmony_ci } else { 25662306a36Sopenharmony_ci folio = folio0; 25762306a36Sopenharmony_ci } 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci block = kmap_local_folio(folio, b * AFS_DIR_BLOCK_SIZE - folio_file_pos(folio)); 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci /* Abandon the edit if we got a callback break. */ 26262306a36Sopenharmony_ci if (!test_bit(AFS_VNODE_DIR_VALID, &vnode->flags)) 26362306a36Sopenharmony_ci goto invalidated; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci _debug("block %u: %2u %3u %u", 26662306a36Sopenharmony_ci b, 26762306a36Sopenharmony_ci (b < AFS_DIR_BLOCKS_WITH_CTR) ? meta->meta.alloc_ctrs[b] : 99, 26862306a36Sopenharmony_ci ntohs(block->hdr.npages), 26962306a36Sopenharmony_ci ntohs(block->hdr.magic)); 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci /* Initialise the block if necessary. */ 27262306a36Sopenharmony_ci if (b == nr_blocks) { 27362306a36Sopenharmony_ci _debug("init %u", b); 27462306a36Sopenharmony_ci afs_edit_init_block(meta, block, b); 27562306a36Sopenharmony_ci afs_set_i_size(vnode, (b + 1) * AFS_DIR_BLOCK_SIZE); 27662306a36Sopenharmony_ci } 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci /* Only lower dir blocks have a counter in the header. */ 27962306a36Sopenharmony_ci if (b >= AFS_DIR_BLOCKS_WITH_CTR || 28062306a36Sopenharmony_ci meta->meta.alloc_ctrs[b] >= need_slots) { 28162306a36Sopenharmony_ci /* We need to try and find one or more consecutive 28262306a36Sopenharmony_ci * slots to hold the entry. 28362306a36Sopenharmony_ci */ 28462306a36Sopenharmony_ci slot = afs_find_contig_bits(block, need_slots); 28562306a36Sopenharmony_ci if (slot >= 0) { 28662306a36Sopenharmony_ci _debug("slot %u", slot); 28762306a36Sopenharmony_ci goto found_space; 28862306a36Sopenharmony_ci } 28962306a36Sopenharmony_ci } 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci kunmap_local(block); 29262306a36Sopenharmony_ci if (folio != folio0) { 29362306a36Sopenharmony_ci folio_unlock(folio); 29462306a36Sopenharmony_ci folio_put(folio); 29562306a36Sopenharmony_ci } 29662306a36Sopenharmony_ci } 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci /* There are no spare slots of sufficient size, yet the operation 29962306a36Sopenharmony_ci * succeeded. Download the directory again. 30062306a36Sopenharmony_ci */ 30162306a36Sopenharmony_ci trace_afs_edit_dir(vnode, why, afs_edit_dir_create_nospc, 0, 0, 0, 0, name->name); 30262306a36Sopenharmony_ci clear_bit(AFS_VNODE_DIR_VALID, &vnode->flags); 30362306a36Sopenharmony_ci goto out_unmap; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_cinew_directory: 30662306a36Sopenharmony_ci afs_edit_init_block(meta, meta, 0); 30762306a36Sopenharmony_ci i_size = AFS_DIR_BLOCK_SIZE; 30862306a36Sopenharmony_ci afs_set_i_size(vnode, i_size); 30962306a36Sopenharmony_ci slot = AFS_DIR_RESV_BLOCKS0; 31062306a36Sopenharmony_ci folio = folio0; 31162306a36Sopenharmony_ci block = kmap_local_folio(folio, 0); 31262306a36Sopenharmony_ci nr_blocks = 1; 31362306a36Sopenharmony_ci b = 0; 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_cifound_space: 31662306a36Sopenharmony_ci /* Set the dirent slot. */ 31762306a36Sopenharmony_ci trace_afs_edit_dir(vnode, why, afs_edit_dir_create, b, slot, 31862306a36Sopenharmony_ci new_fid->vnode, new_fid->unique, name->name); 31962306a36Sopenharmony_ci de = &block->dirents[slot]; 32062306a36Sopenharmony_ci de->u.valid = 1; 32162306a36Sopenharmony_ci de->u.unused[0] = 0; 32262306a36Sopenharmony_ci de->u.hash_next = 0; // TODO: Really need to maintain this 32362306a36Sopenharmony_ci de->u.vnode = htonl(new_fid->vnode); 32462306a36Sopenharmony_ci de->u.unique = htonl(new_fid->unique); 32562306a36Sopenharmony_ci memcpy(de->u.name, name->name, name->len + 1); 32662306a36Sopenharmony_ci de->u.name[name->len] = 0; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci /* Adjust the bitmap. */ 32962306a36Sopenharmony_ci afs_set_contig_bits(block, slot, need_slots); 33062306a36Sopenharmony_ci kunmap_local(block); 33162306a36Sopenharmony_ci if (folio != folio0) { 33262306a36Sopenharmony_ci folio_unlock(folio); 33362306a36Sopenharmony_ci folio_put(folio); 33462306a36Sopenharmony_ci } 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci /* Adjust the allocation counter. */ 33762306a36Sopenharmony_ci if (b < AFS_DIR_BLOCKS_WITH_CTR) 33862306a36Sopenharmony_ci meta->meta.alloc_ctrs[b] -= need_slots; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci inode_inc_iversion_raw(&vnode->netfs.inode); 34162306a36Sopenharmony_ci afs_stat_v(vnode, n_dir_cr); 34262306a36Sopenharmony_ci _debug("Insert %s in %u[%u]", name->name, b, slot); 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ciout_unmap: 34562306a36Sopenharmony_ci kunmap_local(meta); 34662306a36Sopenharmony_ci folio_unlock(folio0); 34762306a36Sopenharmony_ci folio_put(folio0); 34862306a36Sopenharmony_ci _leave(""); 34962306a36Sopenharmony_ci return; 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ciinvalidated: 35262306a36Sopenharmony_ci trace_afs_edit_dir(vnode, why, afs_edit_dir_create_inval, 0, 0, 0, 0, name->name); 35362306a36Sopenharmony_ci clear_bit(AFS_VNODE_DIR_VALID, &vnode->flags); 35462306a36Sopenharmony_ci kunmap_local(block); 35562306a36Sopenharmony_ci if (folio != folio0) { 35662306a36Sopenharmony_ci folio_unlock(folio); 35762306a36Sopenharmony_ci folio_put(folio); 35862306a36Sopenharmony_ci } 35962306a36Sopenharmony_ci goto out_unmap; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_cierror: 36262306a36Sopenharmony_ci trace_afs_edit_dir(vnode, why, afs_edit_dir_create_error, 0, 0, 0, 0, name->name); 36362306a36Sopenharmony_ci clear_bit(AFS_VNODE_DIR_VALID, &vnode->flags); 36462306a36Sopenharmony_ci goto out_unmap; 36562306a36Sopenharmony_ci} 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci/* 36862306a36Sopenharmony_ci * Edit a directory's file data to remove a new directory entry. Doing this 36962306a36Sopenharmony_ci * after unlink, rmdir or rename if the data version number is incremented by 37062306a36Sopenharmony_ci * exactly one avoids the need to re-download the entire directory contents. 37162306a36Sopenharmony_ci * 37262306a36Sopenharmony_ci * The caller must hold the inode locked. 37362306a36Sopenharmony_ci */ 37462306a36Sopenharmony_civoid afs_edit_dir_remove(struct afs_vnode *vnode, 37562306a36Sopenharmony_ci struct qstr *name, enum afs_edit_dir_reason why) 37662306a36Sopenharmony_ci{ 37762306a36Sopenharmony_ci union afs_xdr_dir_block *meta, *block; 37862306a36Sopenharmony_ci union afs_xdr_dirent *de; 37962306a36Sopenharmony_ci struct folio *folio0, *folio; 38062306a36Sopenharmony_ci unsigned int need_slots, nr_blocks, b; 38162306a36Sopenharmony_ci pgoff_t index; 38262306a36Sopenharmony_ci loff_t i_size; 38362306a36Sopenharmony_ci int slot; 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci _enter(",,{%d,%s},", name->len, name->name); 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci i_size = i_size_read(&vnode->netfs.inode); 38862306a36Sopenharmony_ci if (i_size < AFS_DIR_BLOCK_SIZE || 38962306a36Sopenharmony_ci i_size > AFS_DIR_BLOCK_SIZE * AFS_DIR_MAX_BLOCKS || 39062306a36Sopenharmony_ci (i_size & (AFS_DIR_BLOCK_SIZE - 1))) { 39162306a36Sopenharmony_ci clear_bit(AFS_VNODE_DIR_VALID, &vnode->flags); 39262306a36Sopenharmony_ci return; 39362306a36Sopenharmony_ci } 39462306a36Sopenharmony_ci nr_blocks = i_size / AFS_DIR_BLOCK_SIZE; 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci folio0 = afs_dir_get_folio(vnode, 0); 39762306a36Sopenharmony_ci if (!folio0) { 39862306a36Sopenharmony_ci _leave(" [fgp]"); 39962306a36Sopenharmony_ci return; 40062306a36Sopenharmony_ci } 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci /* Work out how many slots we're going to discard. */ 40362306a36Sopenharmony_ci need_slots = afs_dir_calc_slots(name->len); 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci meta = kmap_local_folio(folio0, 0); 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci /* Find a block that has sufficient slots available. Each folio 40862306a36Sopenharmony_ci * contains two or more directory blocks. 40962306a36Sopenharmony_ci */ 41062306a36Sopenharmony_ci for (b = 0; b < nr_blocks; b++) { 41162306a36Sopenharmony_ci index = b / AFS_DIR_BLOCKS_PER_PAGE; 41262306a36Sopenharmony_ci if (index >= folio_nr_pages(folio0)) { 41362306a36Sopenharmony_ci folio = afs_dir_get_folio(vnode, index); 41462306a36Sopenharmony_ci if (!folio) 41562306a36Sopenharmony_ci goto error; 41662306a36Sopenharmony_ci } else { 41762306a36Sopenharmony_ci folio = folio0; 41862306a36Sopenharmony_ci } 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci block = kmap_local_folio(folio, b * AFS_DIR_BLOCK_SIZE - folio_file_pos(folio)); 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci /* Abandon the edit if we got a callback break. */ 42362306a36Sopenharmony_ci if (!test_bit(AFS_VNODE_DIR_VALID, &vnode->flags)) 42462306a36Sopenharmony_ci goto invalidated; 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci if (b > AFS_DIR_BLOCKS_WITH_CTR || 42762306a36Sopenharmony_ci meta->meta.alloc_ctrs[b] <= AFS_DIR_SLOTS_PER_BLOCK - 1 - need_slots) { 42862306a36Sopenharmony_ci slot = afs_dir_scan_block(block, name, b); 42962306a36Sopenharmony_ci if (slot >= 0) 43062306a36Sopenharmony_ci goto found_dirent; 43162306a36Sopenharmony_ci } 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci kunmap_local(block); 43462306a36Sopenharmony_ci if (folio != folio0) { 43562306a36Sopenharmony_ci folio_unlock(folio); 43662306a36Sopenharmony_ci folio_put(folio); 43762306a36Sopenharmony_ci } 43862306a36Sopenharmony_ci } 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci /* Didn't find the dirent to clobber. Download the directory again. */ 44162306a36Sopenharmony_ci trace_afs_edit_dir(vnode, why, afs_edit_dir_delete_noent, 44262306a36Sopenharmony_ci 0, 0, 0, 0, name->name); 44362306a36Sopenharmony_ci clear_bit(AFS_VNODE_DIR_VALID, &vnode->flags); 44462306a36Sopenharmony_ci goto out_unmap; 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_cifound_dirent: 44762306a36Sopenharmony_ci de = &block->dirents[slot]; 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci trace_afs_edit_dir(vnode, why, afs_edit_dir_delete, b, slot, 45062306a36Sopenharmony_ci ntohl(de->u.vnode), ntohl(de->u.unique), 45162306a36Sopenharmony_ci name->name); 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci memset(de, 0, sizeof(*de) * need_slots); 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci /* Adjust the bitmap. */ 45662306a36Sopenharmony_ci afs_clear_contig_bits(block, slot, need_slots); 45762306a36Sopenharmony_ci kunmap_local(block); 45862306a36Sopenharmony_ci if (folio != folio0) { 45962306a36Sopenharmony_ci folio_unlock(folio); 46062306a36Sopenharmony_ci folio_put(folio); 46162306a36Sopenharmony_ci } 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci /* Adjust the allocation counter. */ 46462306a36Sopenharmony_ci if (b < AFS_DIR_BLOCKS_WITH_CTR) 46562306a36Sopenharmony_ci meta->meta.alloc_ctrs[b] += need_slots; 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci inode_set_iversion_raw(&vnode->netfs.inode, vnode->status.data_version); 46862306a36Sopenharmony_ci afs_stat_v(vnode, n_dir_rm); 46962306a36Sopenharmony_ci _debug("Remove %s from %u[%u]", name->name, b, slot); 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ciout_unmap: 47262306a36Sopenharmony_ci kunmap_local(meta); 47362306a36Sopenharmony_ci folio_unlock(folio0); 47462306a36Sopenharmony_ci folio_put(folio0); 47562306a36Sopenharmony_ci _leave(""); 47662306a36Sopenharmony_ci return; 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ciinvalidated: 47962306a36Sopenharmony_ci trace_afs_edit_dir(vnode, why, afs_edit_dir_delete_inval, 48062306a36Sopenharmony_ci 0, 0, 0, 0, name->name); 48162306a36Sopenharmony_ci clear_bit(AFS_VNODE_DIR_VALID, &vnode->flags); 48262306a36Sopenharmony_ci kunmap_local(block); 48362306a36Sopenharmony_ci if (folio != folio0) { 48462306a36Sopenharmony_ci folio_unlock(folio); 48562306a36Sopenharmony_ci folio_put(folio); 48662306a36Sopenharmony_ci } 48762306a36Sopenharmony_ci goto out_unmap; 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_cierror: 49062306a36Sopenharmony_ci trace_afs_edit_dir(vnode, why, afs_edit_dir_delete_error, 49162306a36Sopenharmony_ci 0, 0, 0, 0, name->name); 49262306a36Sopenharmony_ci clear_bit(AFS_VNODE_DIR_VALID, &vnode->flags); 49362306a36Sopenharmony_ci goto out_unmap; 49462306a36Sopenharmony_ci} 495