// SPDX-License-Identifier: GPL-2.0-or-later /* AFS filesystem directory editing * * Copyright (C) 2018 Red Hat, Inc. All Rights Reserved. * Written by David Howells (dhowells@redhat.com)
*/
/* * Find a number of contiguous clear bits in a directory block bitmask. * * There are 64 slots, which means we can load the entire bitmap into a * variable. The first bit doesn't count as it corresponds to the block header * slot. nr_slots is between 1 and 9.
*/ staticint afs_find_contig_bits(union afs_xdr_dir_block *block, unsignedint nr_slots)
{
u64 bitmap;
u32 mask; int bit, n;
do { if (sizeof(unsignedlong) == 8)
n = ffz(bitmap); else
n = ((u32)bitmap) != 0 ?
ffz((u32)bitmap) :
ffz((u32)(bitmap >> 32)) + 32;
bitmap >>= n;
bit += n;
if ((bitmap & mask) == 0) { if (bit > 64 - nr_slots) return -1; return bit;
}
n = __ffs(bitmap);
bitmap >>= n;
bit += n;
} while (bitmap);
return -1;
}
/* * Set a number of contiguous bits in the directory block bitmap.
*/ staticvoid afs_set_contig_bits(union afs_xdr_dir_block *block, int bit, unsignedint nr_slots)
{
u64 mask;
/* * Clear a number of contiguous bits in the directory block bitmap.
*/ staticvoid afs_clear_contig_bits(union afs_xdr_dir_block *block, int bit, unsignedint nr_slots)
{
u64 mask;
/* * Get a specific block, extending the directory storage to cover it as needed.
*/ staticunion afs_xdr_dir_block *afs_dir_get_block(struct afs_dir_iter *iter, size_t block)
{ struct folio_queue *fq; struct afs_vnode *dvnode = iter->dvnode; struct folio *folio;
size_t blpos = block * AFS_DIR_BLOCK_SIZE;
size_t blend = (block + 1) * AFS_DIR_BLOCK_SIZE, fpos = iter->fpos; int ret;
if (dvnode->directory_size < blend) {
size_t cur_size = dvnode->directory_size;
ret = netfs_alloc_folioq_buffer(
NULL, &dvnode->directory, &cur_size, blend,
mapping_gfp_mask(dvnode->netfs.inode.i_mapping));
dvnode->directory_size = cur_size; if (ret < 0) goto fail;
}
fq = iter->fq; if (!fq)
fq = dvnode->directory;
/* Search the folio queue for the folio containing the block... */ for (; fq; fq = fq->next) { for (int s = iter->fq_slot; s < folioq_count(fq); s++) {
size_t fsize = folioq_folio_size(fq, s);
if (blend <= fpos + fsize) { /* ... and then return the mapped block. */
folio = folioq_folio(fq, s); if (WARN_ON_ONCE(folio_pos(folio) != fpos)) goto fail;
iter->fq = fq;
iter->fq_slot = s;
iter->fpos = fpos; return kmap_local_folio(folio, blpos - fpos);
}
fpos += fsize;
}
iter->fq_slot = 0;
}
/* * Scan a directory block looking for a dirent of the right name.
*/ staticint afs_dir_scan_block(constunion afs_xdr_dir_block *block, conststruct qstr *name, unsignedint blocknum)
{ constunion afs_xdr_dirent *de;
u64 bitmap; int d, len, n;
for (d = (blocknum == 0 ? AFS_DIR_RESV_BLOCKS0 : AFS_DIR_RESV_BLOCKS);
d < AFS_DIR_SLOTS_PER_BLOCK;
d++) { if (!((bitmap >> d) & 1)) continue;
de = &block->dirents[d]; if (de->u.valid != 1) continue;
/* The block was NUL-terminated by afs_dir_check_page(). */
len = strlen(de->u.name); if (len == name->len &&
memcmp(de->u.name, name->name, name->len) == 0) return d;
n = round_up(12 + len + 1 + 4, AFS_DIR_DIRENT_SIZE);
n /= AFS_DIR_DIRENT_SIZE;
d += n - 1;
}
return -1;
}
/* * Initialise a new directory block. Note that block 0 is special and contains * some extra metadata.
*/ staticvoid afs_edit_init_block(union afs_xdr_dir_block *meta, union afs_xdr_dir_block *block, int block_num)
{
memset(block, 0, sizeof(*block));
block->hdr.npages = htons(1);
block->hdr.magic = AFS_DIR_MAGIC;
block->hdr.bitmap[0] = 1;
if (block_num < AFS_DIR_BLOCKS_WITH_CTR)
meta->meta.alloc_ctrs[block_num] =
AFS_DIR_SLOTS_PER_BLOCK - AFS_DIR_RESV_BLOCKS;
}
/* * Edit a directory's file data to add a new directory entry. Doing this after * create, mkdir, symlink, link or rename if the data version number is * incremented by exactly one avoids the need to re-download the entire * directory contents. * * The caller must hold the inode locked.
*/ void afs_edit_dir_add(struct afs_vnode *vnode, struct qstr *name, struct afs_fid *new_fid, enum afs_edit_dir_reason why)
{ union afs_xdr_dir_block *meta, *block; union afs_xdr_dirent *de; struct afs_dir_iter iter = { .dvnode = vnode }; unsignedint nr_blocks, b, entry;
loff_t i_size; int slot;
/* Find a block that has sufficient slots available. Each folio * contains two or more directory blocks.
*/ for (b = 0; b < nr_blocks + 1; b++) { /* If the directory extended into a new folio, then we need to * tack a new folio on the end.
*/ if (nr_blocks >= AFS_DIR_MAX_BLOCKS) goto error_too_many_blocks;
/* Lower dir blocks have a counter in the header we can check. */ if (b < AFS_DIR_BLOCKS_WITH_CTR &&
meta->meta.alloc_ctrs[b] < iter.nr_slots) continue;
block = afs_dir_get_block(&iter, b); if (!block) goto error;
/* Abandon the edit if we got a callback break. */ if (!test_bit(AFS_VNODE_DIR_VALID, &vnode->flags)) goto already_invalidated;
/* Initialise the block if necessary. */ if (b == nr_blocks) {
_debug("init %u", b);
afs_edit_init_block(meta, block, b);
afs_set_i_size(vnode, (b + 1) * AFS_DIR_BLOCK_SIZE);
}
/* We need to try and find one or more consecutive slots to * hold the entry.
*/
slot = afs_find_contig_bits(block, iter.nr_slots); if (slot >= 0) {
_debug("slot %u", slot); goto found_space;
}
kunmap_local(block);
}
/* There are no spare slots of sufficient size, yet the operation * succeeded. Download the directory again.
*/
trace_afs_edit_dir(vnode, why, afs_edit_dir_create_nospc, 0, 0, 0, 0, name->name);
afs_invalidate_dir(vnode, afs_dir_invalid_edit_add_no_slots); goto out_unmap;
/* * Edit a directory's file data to remove a new directory entry. Doing this * after unlink, rmdir or rename if the data version number is incremented by * exactly one avoids the need to re-download the entire directory contents. * * The caller must hold the inode locked.
*/ void afs_edit_dir_remove(struct afs_vnode *vnode, struct qstr *name, enum afs_edit_dir_reason why)
{ union afs_xdr_dir_block *meta, *block, *pblock; union afs_xdr_dirent *de, *pde; struct afs_dir_iter iter = { .dvnode = vnode }; struct afs_fid fid; unsignedint b, slot, entry;
loff_t i_size;
__be16 next; int found;
/* Adjust the bitmap. */
afs_clear_contig_bits(block, slot, iter.nr_slots);
/* Adjust the allocation counter. */ if (b < AFS_DIR_BLOCKS_WITH_CTR)
meta->meta.alloc_ctrs[b] += iter.nr_slots;
/* Clear the constituent entries. */
next = de->u.hash_next;
memset(de, 0, sizeof(*de) * iter.nr_slots);
kunmap_local(block);
/* Adjust the hash chain: if iter->prev_entry is 0, the hashtable head * index is previous; otherwise it's slot number of the previous entry.
*/ if (!iter.prev_entry) {
__be16 prev_next = meta->meta.hashtable[iter.bucket];
/* * Edit a subdirectory that has been moved between directories to update the * ".." entry.
*/ void afs_edit_dir_update_dotdot(struct afs_vnode *vnode, struct afs_vnode *new_dvnode, enum afs_edit_dir_reason why)
{ union afs_xdr_dir_block *block; union afs_xdr_dirent *de; struct afs_dir_iter iter = { .dvnode = vnode }; unsignedint nr_blocks, b;
loff_t i_size; int slot;
/* Find a block that has sufficient slots available. Each folio * contains two or more directory blocks.
*/ for (b = 0; b < nr_blocks; b++) {
block = afs_dir_get_block(&iter, b); if (!block) goto error;
/* Abandon the edit if we got a callback break. */ if (!test_bit(AFS_VNODE_DIR_VALID, &vnode->flags)) goto already_invalidated;
/* * Initialise a new directory. We need to fill in the "." and ".." entries.
*/ void afs_mkdir_init_dir(struct afs_vnode *dvnode, struct afs_vnode *parent_dvnode)
{ union afs_xdr_dir_block *meta; struct afs_dir_iter iter = { .dvnode = dvnode }; union afs_xdr_dirent *de; unsignedint slot = AFS_DIR_RESV_BLOCKS0;
loff_t i_size;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.