1b4d0d230SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
263a4681fSDavid Howells /* AFS filesystem directory editing
363a4681fSDavid Howells *
463a4681fSDavid Howells * Copyright (C) 2018 Red Hat, Inc. All Rights Reserved.
563a4681fSDavid Howells * Written by David Howells (dhowells@redhat.com)
663a4681fSDavid Howells */
763a4681fSDavid Howells
863a4681fSDavid Howells #include <linux/kernel.h>
963a4681fSDavid Howells #include <linux/fs.h>
1063a4681fSDavid Howells #include <linux/namei.h>
1163a4681fSDavid Howells #include <linux/pagemap.h>
1263a4681fSDavid Howells #include <linux/iversion.h>
1363a4681fSDavid Howells #include "internal.h"
1463a4681fSDavid Howells #include "xdr_fs.h"
1563a4681fSDavid Howells
1663a4681fSDavid Howells /*
1763a4681fSDavid Howells * Find a number of contiguous clear bits in a directory block bitmask.
1863a4681fSDavid Howells *
1963a4681fSDavid Howells * There are 64 slots, which means we can load the entire bitmap into a
2063a4681fSDavid Howells * variable. The first bit doesn't count as it corresponds to the block header
2163a4681fSDavid Howells * slot. nr_slots is between 1 and 9.
2263a4681fSDavid Howells */
afs_find_contig_bits(union afs_xdr_dir_block * block,unsigned int nr_slots)2363a4681fSDavid Howells static int afs_find_contig_bits(union afs_xdr_dir_block *block, unsigned int nr_slots)
2463a4681fSDavid Howells {
2563a4681fSDavid Howells u64 bitmap;
2663a4681fSDavid Howells u32 mask;
2763a4681fSDavid Howells int bit, n;
2863a4681fSDavid Howells
2963a4681fSDavid Howells bitmap = (u64)block->hdr.bitmap[0] << 0 * 8;
3063a4681fSDavid Howells bitmap |= (u64)block->hdr.bitmap[1] << 1 * 8;
3163a4681fSDavid Howells bitmap |= (u64)block->hdr.bitmap[2] << 2 * 8;
3263a4681fSDavid Howells bitmap |= (u64)block->hdr.bitmap[3] << 3 * 8;
3363a4681fSDavid Howells bitmap |= (u64)block->hdr.bitmap[4] << 4 * 8;
3463a4681fSDavid Howells bitmap |= (u64)block->hdr.bitmap[5] << 5 * 8;
3563a4681fSDavid Howells bitmap |= (u64)block->hdr.bitmap[6] << 6 * 8;
3663a4681fSDavid Howells bitmap |= (u64)block->hdr.bitmap[7] << 7 * 8;
3763a4681fSDavid Howells bitmap >>= 1; /* The first entry is metadata */
3863a4681fSDavid Howells bit = 1;
3963a4681fSDavid Howells mask = (1 << nr_slots) - 1;
4063a4681fSDavid Howells
4163a4681fSDavid Howells do {
4263a4681fSDavid Howells if (sizeof(unsigned long) == 8)
4363a4681fSDavid Howells n = ffz(bitmap);
4463a4681fSDavid Howells else
4563a4681fSDavid Howells n = ((u32)bitmap) != 0 ?
4663a4681fSDavid Howells ffz((u32)bitmap) :
4763a4681fSDavid Howells ffz((u32)(bitmap >> 32)) + 32;
4863a4681fSDavid Howells bitmap >>= n;
4963a4681fSDavid Howells bit += n;
5063a4681fSDavid Howells
5163a4681fSDavid Howells if ((bitmap & mask) == 0) {
5263a4681fSDavid Howells if (bit > 64 - nr_slots)
5363a4681fSDavid Howells return -1;
5463a4681fSDavid Howells return bit;
5563a4681fSDavid Howells }
5663a4681fSDavid Howells
5763a4681fSDavid Howells n = __ffs(bitmap);
5863a4681fSDavid Howells bitmap >>= n;
5963a4681fSDavid Howells bit += n;
6063a4681fSDavid Howells } while (bitmap);
6163a4681fSDavid Howells
6263a4681fSDavid Howells return -1;
6363a4681fSDavid Howells }
6463a4681fSDavid Howells
6563a4681fSDavid Howells /*
6663a4681fSDavid Howells * Set a number of contiguous bits in the directory block bitmap.
6763a4681fSDavid Howells */
afs_set_contig_bits(union afs_xdr_dir_block * block,int bit,unsigned int nr_slots)6863a4681fSDavid Howells static void afs_set_contig_bits(union afs_xdr_dir_block *block,
6963a4681fSDavid Howells int bit, unsigned int nr_slots)
7063a4681fSDavid Howells {
7151590df4Szhengbin u64 mask;
7263a4681fSDavid Howells
7363a4681fSDavid Howells mask = (1 << nr_slots) - 1;
7463a4681fSDavid Howells mask <<= bit;
7563a4681fSDavid Howells
7663a4681fSDavid Howells block->hdr.bitmap[0] |= (u8)(mask >> 0 * 8);
7763a4681fSDavid Howells block->hdr.bitmap[1] |= (u8)(mask >> 1 * 8);
7863a4681fSDavid Howells block->hdr.bitmap[2] |= (u8)(mask >> 2 * 8);
7963a4681fSDavid Howells block->hdr.bitmap[3] |= (u8)(mask >> 3 * 8);
8063a4681fSDavid Howells block->hdr.bitmap[4] |= (u8)(mask >> 4 * 8);
8163a4681fSDavid Howells block->hdr.bitmap[5] |= (u8)(mask >> 5 * 8);
8263a4681fSDavid Howells block->hdr.bitmap[6] |= (u8)(mask >> 6 * 8);
8363a4681fSDavid Howells block->hdr.bitmap[7] |= (u8)(mask >> 7 * 8);
8463a4681fSDavid Howells }
8563a4681fSDavid Howells
8663a4681fSDavid Howells /*
8763a4681fSDavid Howells * Clear a number of contiguous bits in the directory block bitmap.
8863a4681fSDavid Howells */
afs_clear_contig_bits(union afs_xdr_dir_block * block,int bit,unsigned int nr_slots)8963a4681fSDavid Howells static void afs_clear_contig_bits(union afs_xdr_dir_block *block,
9063a4681fSDavid Howells int bit, unsigned int nr_slots)
9163a4681fSDavid Howells {
9251590df4Szhengbin u64 mask;
9363a4681fSDavid Howells
9463a4681fSDavid Howells mask = (1 << nr_slots) - 1;
9563a4681fSDavid Howells mask <<= bit;
9663a4681fSDavid Howells
9763a4681fSDavid Howells block->hdr.bitmap[0] &= ~(u8)(mask >> 0 * 8);
9863a4681fSDavid Howells block->hdr.bitmap[1] &= ~(u8)(mask >> 1 * 8);
9963a4681fSDavid Howells block->hdr.bitmap[2] &= ~(u8)(mask >> 2 * 8);
10063a4681fSDavid Howells block->hdr.bitmap[3] &= ~(u8)(mask >> 3 * 8);
10163a4681fSDavid Howells block->hdr.bitmap[4] &= ~(u8)(mask >> 4 * 8);
10263a4681fSDavid Howells block->hdr.bitmap[5] &= ~(u8)(mask >> 5 * 8);
10363a4681fSDavid Howells block->hdr.bitmap[6] &= ~(u8)(mask >> 6 * 8);
10463a4681fSDavid Howells block->hdr.bitmap[7] &= ~(u8)(mask >> 7 * 8);
10563a4681fSDavid Howells }
10663a4681fSDavid Howells
10763a4681fSDavid Howells /*
108255ed636SDavid Howells * Get a new directory folio.
109255ed636SDavid Howells */
afs_dir_get_folio(struct afs_vnode * vnode,pgoff_t index)110255ed636SDavid Howells static struct folio *afs_dir_get_folio(struct afs_vnode *vnode, pgoff_t index)
111255ed636SDavid Howells {
112874c8ca1SDavid Howells struct address_space *mapping = vnode->netfs.inode.i_mapping;
113255ed636SDavid Howells struct folio *folio;
114255ed636SDavid Howells
115255ed636SDavid Howells folio = __filemap_get_folio(mapping, index,
116255ed636SDavid Howells FGP_LOCK | FGP_ACCESSED | FGP_CREAT,
117255ed636SDavid Howells mapping->gfp_mask);
11858f5f669SChristoph Hellwig if (IS_ERR(folio)) {
119255ed636SDavid Howells clear_bit(AFS_VNODE_DIR_VALID, &vnode->flags);
12058f5f669SChristoph Hellwig return NULL;
12158f5f669SChristoph Hellwig }
12258f5f669SChristoph Hellwig if (!folio_test_private(folio))
123255ed636SDavid Howells folio_attach_private(folio, (void *)1);
124255ed636SDavid Howells return folio;
125255ed636SDavid Howells }
126255ed636SDavid Howells
127255ed636SDavid Howells /*
12863a4681fSDavid Howells * Scan a directory block looking for a dirent of the right name.
12963a4681fSDavid Howells */
afs_dir_scan_block(const union afs_xdr_dir_block * block,const struct qstr * name,unsigned int blocknum)130*790dc90bSDavid Howells static int afs_dir_scan_block(const union afs_xdr_dir_block *block, const struct qstr *name,
13163a4681fSDavid Howells unsigned int blocknum)
13263a4681fSDavid Howells {
133*790dc90bSDavid Howells const union afs_xdr_dirent *de;
13463a4681fSDavid Howells u64 bitmap;
13563a4681fSDavid Howells int d, len, n;
13663a4681fSDavid Howells
13763a4681fSDavid Howells _enter("");
13863a4681fSDavid Howells
13963a4681fSDavid Howells bitmap = (u64)block->hdr.bitmap[0] << 0 * 8;
14063a4681fSDavid Howells bitmap |= (u64)block->hdr.bitmap[1] << 1 * 8;
14163a4681fSDavid Howells bitmap |= (u64)block->hdr.bitmap[2] << 2 * 8;
14263a4681fSDavid Howells bitmap |= (u64)block->hdr.bitmap[3] << 3 * 8;
14363a4681fSDavid Howells bitmap |= (u64)block->hdr.bitmap[4] << 4 * 8;
14463a4681fSDavid Howells bitmap |= (u64)block->hdr.bitmap[5] << 5 * 8;
14563a4681fSDavid Howells bitmap |= (u64)block->hdr.bitmap[6] << 6 * 8;
14663a4681fSDavid Howells bitmap |= (u64)block->hdr.bitmap[7] << 7 * 8;
14763a4681fSDavid Howells
14863a4681fSDavid Howells for (d = (blocknum == 0 ? AFS_DIR_RESV_BLOCKS0 : AFS_DIR_RESV_BLOCKS);
14963a4681fSDavid Howells d < AFS_DIR_SLOTS_PER_BLOCK;
15063a4681fSDavid Howells d++) {
15163a4681fSDavid Howells if (!((bitmap >> d) & 1))
15263a4681fSDavid Howells continue;
15363a4681fSDavid Howells de = &block->dirents[d];
15463a4681fSDavid Howells if (de->u.valid != 1)
15563a4681fSDavid Howells continue;
15663a4681fSDavid Howells
15763a4681fSDavid Howells /* The block was NUL-terminated by afs_dir_check_page(). */
15863a4681fSDavid Howells len = strlen(de->u.name);
15963a4681fSDavid Howells if (len == name->len &&
16063a4681fSDavid Howells memcmp(de->u.name, name->name, name->len) == 0)
16163a4681fSDavid Howells return d;
16263a4681fSDavid Howells
16363a4681fSDavid Howells n = round_up(12 + len + 1 + 4, AFS_DIR_DIRENT_SIZE);
16463a4681fSDavid Howells n /= AFS_DIR_DIRENT_SIZE;
16563a4681fSDavid Howells d += n - 1;
16663a4681fSDavid Howells }
16763a4681fSDavid Howells
16863a4681fSDavid Howells return -1;
16963a4681fSDavid Howells }
17063a4681fSDavid Howells
17163a4681fSDavid Howells /*
17263a4681fSDavid Howells * Initialise a new directory block. Note that block 0 is special and contains
17363a4681fSDavid Howells * some extra metadata.
17463a4681fSDavid Howells */
afs_edit_init_block(union afs_xdr_dir_block * meta,union afs_xdr_dir_block * block,int block_num)17563a4681fSDavid Howells static void afs_edit_init_block(union afs_xdr_dir_block *meta,
17663a4681fSDavid Howells union afs_xdr_dir_block *block, int block_num)
17763a4681fSDavid Howells {
17863a4681fSDavid Howells memset(block, 0, sizeof(*block));
17963a4681fSDavid Howells block->hdr.npages = htons(1);
18063a4681fSDavid Howells block->hdr.magic = AFS_DIR_MAGIC;
18163a4681fSDavid Howells block->hdr.bitmap[0] = 1;
18263a4681fSDavid Howells
18363a4681fSDavid Howells if (block_num == 0) {
18463a4681fSDavid Howells block->hdr.bitmap[0] = 0xff;
18563a4681fSDavid Howells block->hdr.bitmap[1] = 0x1f;
18663a4681fSDavid Howells memset(block->meta.alloc_ctrs,
18763a4681fSDavid Howells AFS_DIR_SLOTS_PER_BLOCK,
18863a4681fSDavid Howells sizeof(block->meta.alloc_ctrs));
18963a4681fSDavid Howells meta->meta.alloc_ctrs[0] =
19063a4681fSDavid Howells AFS_DIR_SLOTS_PER_BLOCK - AFS_DIR_RESV_BLOCKS0;
19163a4681fSDavid Howells }
19263a4681fSDavid Howells
19363a4681fSDavid Howells if (block_num < AFS_DIR_BLOCKS_WITH_CTR)
19463a4681fSDavid Howells meta->meta.alloc_ctrs[block_num] =
19563a4681fSDavid Howells AFS_DIR_SLOTS_PER_BLOCK - AFS_DIR_RESV_BLOCKS;
19663a4681fSDavid Howells }
19763a4681fSDavid Howells
19863a4681fSDavid Howells /*
19963a4681fSDavid Howells * Edit a directory's file data to add a new directory entry. Doing this after
20063a4681fSDavid Howells * create, mkdir, symlink, link or rename if the data version number is
20163a4681fSDavid Howells * incremented by exactly one avoids the need to re-download the entire
20263a4681fSDavid Howells * directory contents.
20363a4681fSDavid Howells *
20463a4681fSDavid Howells * The caller must hold the inode locked.
20563a4681fSDavid Howells */
afs_edit_dir_add(struct afs_vnode * vnode,struct qstr * name,struct afs_fid * new_fid,enum afs_edit_dir_reason why)20663a4681fSDavid Howells void afs_edit_dir_add(struct afs_vnode *vnode,
20763a4681fSDavid Howells struct qstr *name, struct afs_fid *new_fid,
20863a4681fSDavid Howells enum afs_edit_dir_reason why)
20963a4681fSDavid Howells {
21063a4681fSDavid Howells union afs_xdr_dir_block *meta, *block;
21163a4681fSDavid Howells union afs_xdr_dirent *de;
212255ed636SDavid Howells struct folio *folio0, *folio;
21363a4681fSDavid Howells unsigned int need_slots, nr_blocks, b;
21463a4681fSDavid Howells pgoff_t index;
21563a4681fSDavid Howells loff_t i_size;
21663a4681fSDavid Howells int slot;
21763a4681fSDavid Howells
21863a4681fSDavid Howells _enter(",,{%d,%s},", name->len, name->name);
21963a4681fSDavid Howells
220874c8ca1SDavid Howells i_size = i_size_read(&vnode->netfs.inode);
22163a4681fSDavid Howells if (i_size > AFS_DIR_BLOCK_SIZE * AFS_DIR_MAX_BLOCKS ||
22263a4681fSDavid Howells (i_size & (AFS_DIR_BLOCK_SIZE - 1))) {
22363a4681fSDavid Howells clear_bit(AFS_VNODE_DIR_VALID, &vnode->flags);
22463a4681fSDavid Howells return;
22563a4681fSDavid Howells }
22663a4681fSDavid Howells
227255ed636SDavid Howells folio0 = afs_dir_get_folio(vnode, 0);
228255ed636SDavid Howells if (!folio0) {
22963a4681fSDavid Howells _leave(" [fgp]");
23063a4681fSDavid Howells return;
23163a4681fSDavid Howells }
23263a4681fSDavid Howells
23363a4681fSDavid Howells /* Work out how many slots we're going to need. */
234366911cdSDavid Howells need_slots = afs_dir_calc_slots(name->len);
23563a4681fSDavid Howells
236255ed636SDavid Howells meta = kmap_local_folio(folio0, 0);
23763a4681fSDavid Howells if (i_size == 0)
23863a4681fSDavid Howells goto new_directory;
23963a4681fSDavid Howells nr_blocks = i_size / AFS_DIR_BLOCK_SIZE;
24063a4681fSDavid Howells
241255ed636SDavid Howells /* Find a block that has sufficient slots available. Each folio
24263a4681fSDavid Howells * contains two or more directory blocks.
24363a4681fSDavid Howells */
24463a4681fSDavid Howells for (b = 0; b < nr_blocks + 1; b++) {
245255ed636SDavid Howells /* If the directory extended into a new folio, then we need to
246255ed636SDavid Howells * tack a new folio on the end.
24763a4681fSDavid Howells */
24863a4681fSDavid Howells index = b / AFS_DIR_BLOCKS_PER_PAGE;
24963a4681fSDavid Howells if (nr_blocks >= AFS_DIR_MAX_BLOCKS)
25063a4681fSDavid Howells goto error;
251255ed636SDavid Howells if (index >= folio_nr_pages(folio0)) {
252255ed636SDavid Howells folio = afs_dir_get_folio(vnode, index);
253255ed636SDavid Howells if (!folio)
25463a4681fSDavid Howells goto error;
255255ed636SDavid Howells } else {
256255ed636SDavid Howells folio = folio0;
25763a4681fSDavid Howells }
25863a4681fSDavid Howells
259255ed636SDavid Howells block = kmap_local_folio(folio, b * AFS_DIR_BLOCK_SIZE - folio_file_pos(folio));
260255ed636SDavid Howells
26163a4681fSDavid Howells /* Abandon the edit if we got a callback break. */
26263a4681fSDavid Howells if (!test_bit(AFS_VNODE_DIR_VALID, &vnode->flags))
26363a4681fSDavid Howells goto invalidated;
26463a4681fSDavid Howells
26563a4681fSDavid Howells _debug("block %u: %2u %3u %u",
26663a4681fSDavid Howells b,
26763a4681fSDavid Howells (b < AFS_DIR_BLOCKS_WITH_CTR) ? meta->meta.alloc_ctrs[b] : 99,
26863a4681fSDavid Howells ntohs(block->hdr.npages),
26963a4681fSDavid Howells ntohs(block->hdr.magic));
27063a4681fSDavid Howells
27163a4681fSDavid Howells /* Initialise the block if necessary. */
27263a4681fSDavid Howells if (b == nr_blocks) {
27363a4681fSDavid Howells _debug("init %u", b);
27463a4681fSDavid Howells afs_edit_init_block(meta, block, b);
2759d37e1caSDavid Howells afs_set_i_size(vnode, (b + 1) * AFS_DIR_BLOCK_SIZE);
27663a4681fSDavid Howells }
27763a4681fSDavid Howells
278255ed636SDavid Howells /* Only lower dir blocks have a counter in the header. */
27963a4681fSDavid Howells if (b >= AFS_DIR_BLOCKS_WITH_CTR ||
28063a4681fSDavid Howells meta->meta.alloc_ctrs[b] >= need_slots) {
28163a4681fSDavid Howells /* We need to try and find one or more consecutive
28263a4681fSDavid Howells * slots to hold the entry.
28363a4681fSDavid Howells */
28463a4681fSDavid Howells slot = afs_find_contig_bits(block, need_slots);
28563a4681fSDavid Howells if (slot >= 0) {
28663a4681fSDavid Howells _debug("slot %u", slot);
28763a4681fSDavid Howells goto found_space;
28863a4681fSDavid Howells }
28963a4681fSDavid Howells }
29063a4681fSDavid Howells
291255ed636SDavid Howells kunmap_local(block);
292255ed636SDavid Howells if (folio != folio0) {
293255ed636SDavid Howells folio_unlock(folio);
294255ed636SDavid Howells folio_put(folio);
29563a4681fSDavid Howells }
29663a4681fSDavid Howells }
29763a4681fSDavid Howells
29863a4681fSDavid Howells /* There are no spare slots of sufficient size, yet the operation
29963a4681fSDavid Howells * succeeded. Download the directory again.
30063a4681fSDavid Howells */
30163a4681fSDavid Howells trace_afs_edit_dir(vnode, why, afs_edit_dir_create_nospc, 0, 0, 0, 0, name->name);
30263a4681fSDavid Howells clear_bit(AFS_VNODE_DIR_VALID, &vnode->flags);
30363a4681fSDavid Howells goto out_unmap;
30463a4681fSDavid Howells
30563a4681fSDavid Howells new_directory:
30663a4681fSDavid Howells afs_edit_init_block(meta, meta, 0);
30763a4681fSDavid Howells i_size = AFS_DIR_BLOCK_SIZE;
3089d37e1caSDavid Howells afs_set_i_size(vnode, i_size);
30963a4681fSDavid Howells slot = AFS_DIR_RESV_BLOCKS0;
310255ed636SDavid Howells folio = folio0;
311255ed636SDavid Howells block = kmap_local_folio(folio, 0);
31263a4681fSDavid Howells nr_blocks = 1;
31363a4681fSDavid Howells b = 0;
31463a4681fSDavid Howells
31563a4681fSDavid Howells found_space:
31663a4681fSDavid Howells /* Set the dirent slot. */
31763a4681fSDavid Howells trace_afs_edit_dir(vnode, why, afs_edit_dir_create, b, slot,
31863a4681fSDavid Howells new_fid->vnode, new_fid->unique, name->name);
31963a4681fSDavid Howells de = &block->dirents[slot];
32063a4681fSDavid Howells de->u.valid = 1;
32163a4681fSDavid Howells de->u.unused[0] = 0;
32263a4681fSDavid Howells de->u.hash_next = 0; // TODO: Really need to maintain this
32363a4681fSDavid Howells de->u.vnode = htonl(new_fid->vnode);
32463a4681fSDavid Howells de->u.unique = htonl(new_fid->unique);
32563a4681fSDavid Howells memcpy(de->u.name, name->name, name->len + 1);
32663a4681fSDavid Howells de->u.name[name->len] = 0;
32763a4681fSDavid Howells
32863a4681fSDavid Howells /* Adjust the bitmap. */
32963a4681fSDavid Howells afs_set_contig_bits(block, slot, need_slots);
330255ed636SDavid Howells kunmap_local(block);
331255ed636SDavid Howells if (folio != folio0) {
332255ed636SDavid Howells folio_unlock(folio);
333255ed636SDavid Howells folio_put(folio);
33463a4681fSDavid Howells }
33563a4681fSDavid Howells
33663a4681fSDavid Howells /* Adjust the allocation counter. */
33763a4681fSDavid Howells if (b < AFS_DIR_BLOCKS_WITH_CTR)
33863a4681fSDavid Howells meta->meta.alloc_ctrs[b] -= need_slots;
33963a4681fSDavid Howells
340874c8ca1SDavid Howells inode_inc_iversion_raw(&vnode->netfs.inode);
34163a4681fSDavid Howells afs_stat_v(vnode, n_dir_cr);
34263a4681fSDavid Howells _debug("Insert %s in %u[%u]", name->name, b, slot);
34363a4681fSDavid Howells
34463a4681fSDavid Howells out_unmap:
345255ed636SDavid Howells kunmap_local(meta);
346255ed636SDavid Howells folio_unlock(folio0);
347255ed636SDavid Howells folio_put(folio0);
34863a4681fSDavid Howells _leave("");
34963a4681fSDavid Howells return;
35063a4681fSDavid Howells
35163a4681fSDavid Howells invalidated:
35263a4681fSDavid Howells trace_afs_edit_dir(vnode, why, afs_edit_dir_create_inval, 0, 0, 0, 0, name->name);
35363a4681fSDavid Howells clear_bit(AFS_VNODE_DIR_VALID, &vnode->flags);
354255ed636SDavid Howells kunmap_local(block);
355255ed636SDavid Howells if (folio != folio0) {
356255ed636SDavid Howells folio_unlock(folio);
357255ed636SDavid Howells folio_put(folio);
35863a4681fSDavid Howells }
35963a4681fSDavid Howells goto out_unmap;
36063a4681fSDavid Howells
36163a4681fSDavid Howells error:
36263a4681fSDavid Howells trace_afs_edit_dir(vnode, why, afs_edit_dir_create_error, 0, 0, 0, 0, name->name);
36363a4681fSDavid Howells clear_bit(AFS_VNODE_DIR_VALID, &vnode->flags);
36463a4681fSDavid Howells goto out_unmap;
36563a4681fSDavid Howells }
36663a4681fSDavid Howells
36763a4681fSDavid Howells /*
36863a4681fSDavid Howells * Edit a directory's file data to remove a new directory entry. Doing this
36963a4681fSDavid Howells * after unlink, rmdir or rename if the data version number is incremented by
37063a4681fSDavid Howells * exactly one avoids the need to re-download the entire directory contents.
37163a4681fSDavid Howells *
37263a4681fSDavid Howells * The caller must hold the inode locked.
37363a4681fSDavid Howells */
afs_edit_dir_remove(struct afs_vnode * vnode,struct qstr * name,enum afs_edit_dir_reason why)37463a4681fSDavid Howells void afs_edit_dir_remove(struct afs_vnode *vnode,
37563a4681fSDavid Howells struct qstr *name, enum afs_edit_dir_reason why)
37663a4681fSDavid Howells {
37763a4681fSDavid Howells union afs_xdr_dir_block *meta, *block;
37863a4681fSDavid Howells union afs_xdr_dirent *de;
379255ed636SDavid Howells struct folio *folio0, *folio;
38063a4681fSDavid Howells unsigned int need_slots, nr_blocks, b;
38163a4681fSDavid Howells pgoff_t index;
38263a4681fSDavid Howells loff_t i_size;
38363a4681fSDavid Howells int slot;
38463a4681fSDavid Howells
38563a4681fSDavid Howells _enter(",,{%d,%s},", name->len, name->name);
38663a4681fSDavid Howells
387874c8ca1SDavid Howells i_size = i_size_read(&vnode->netfs.inode);
38863a4681fSDavid Howells if (i_size < AFS_DIR_BLOCK_SIZE ||
38963a4681fSDavid Howells i_size > AFS_DIR_BLOCK_SIZE * AFS_DIR_MAX_BLOCKS ||
39063a4681fSDavid Howells (i_size & (AFS_DIR_BLOCK_SIZE - 1))) {
39163a4681fSDavid Howells clear_bit(AFS_VNODE_DIR_VALID, &vnode->flags);
39263a4681fSDavid Howells return;
39363a4681fSDavid Howells }
39463a4681fSDavid Howells nr_blocks = i_size / AFS_DIR_BLOCK_SIZE;
39563a4681fSDavid Howells
396255ed636SDavid Howells folio0 = afs_dir_get_folio(vnode, 0);
397255ed636SDavid Howells if (!folio0) {
39863a4681fSDavid Howells _leave(" [fgp]");
39963a4681fSDavid Howells return;
40063a4681fSDavid Howells }
40163a4681fSDavid Howells
40263a4681fSDavid Howells /* Work out how many slots we're going to discard. */
403366911cdSDavid Howells need_slots = afs_dir_calc_slots(name->len);
40463a4681fSDavid Howells
405255ed636SDavid Howells meta = kmap_local_folio(folio0, 0);
40663a4681fSDavid Howells
407255ed636SDavid Howells /* Find a block that has sufficient slots available. Each folio
40863a4681fSDavid Howells * contains two or more directory blocks.
40963a4681fSDavid Howells */
41063a4681fSDavid Howells for (b = 0; b < nr_blocks; b++) {
41163a4681fSDavid Howells index = b / AFS_DIR_BLOCKS_PER_PAGE;
412255ed636SDavid Howells if (index >= folio_nr_pages(folio0)) {
413255ed636SDavid Howells folio = afs_dir_get_folio(vnode, index);
414255ed636SDavid Howells if (!folio)
41563a4681fSDavid Howells goto error;
41663a4681fSDavid Howells } else {
417255ed636SDavid Howells folio = folio0;
41863a4681fSDavid Howells }
41963a4681fSDavid Howells
420255ed636SDavid Howells block = kmap_local_folio(folio, b * AFS_DIR_BLOCK_SIZE - folio_file_pos(folio));
421255ed636SDavid Howells
42263a4681fSDavid Howells /* Abandon the edit if we got a callback break. */
42363a4681fSDavid Howells if (!test_bit(AFS_VNODE_DIR_VALID, &vnode->flags))
42463a4681fSDavid Howells goto invalidated;
42563a4681fSDavid Howells
42663a4681fSDavid Howells if (b > AFS_DIR_BLOCKS_WITH_CTR ||
42763a4681fSDavid Howells meta->meta.alloc_ctrs[b] <= AFS_DIR_SLOTS_PER_BLOCK - 1 - need_slots) {
42863a4681fSDavid Howells slot = afs_dir_scan_block(block, name, b);
42963a4681fSDavid Howells if (slot >= 0)
43063a4681fSDavid Howells goto found_dirent;
43163a4681fSDavid Howells }
43263a4681fSDavid Howells
433255ed636SDavid Howells kunmap_local(block);
434255ed636SDavid Howells if (folio != folio0) {
435255ed636SDavid Howells folio_unlock(folio);
436255ed636SDavid Howells folio_put(folio);
43763a4681fSDavid Howells }
43863a4681fSDavid Howells }
43963a4681fSDavid Howells
44063a4681fSDavid Howells /* Didn't find the dirent to clobber. Download the directory again. */
44163a4681fSDavid Howells trace_afs_edit_dir(vnode, why, afs_edit_dir_delete_noent,
44263a4681fSDavid Howells 0, 0, 0, 0, name->name);
44363a4681fSDavid Howells clear_bit(AFS_VNODE_DIR_VALID, &vnode->flags);
44463a4681fSDavid Howells goto out_unmap;
44563a4681fSDavid Howells
44663a4681fSDavid Howells found_dirent:
44763a4681fSDavid Howells de = &block->dirents[slot];
44863a4681fSDavid Howells
44963a4681fSDavid Howells trace_afs_edit_dir(vnode, why, afs_edit_dir_delete, b, slot,
45063a4681fSDavid Howells ntohl(de->u.vnode), ntohl(de->u.unique),
45163a4681fSDavid Howells name->name);
45263a4681fSDavid Howells
45363a4681fSDavid Howells memset(de, 0, sizeof(*de) * need_slots);
45463a4681fSDavid Howells
45563a4681fSDavid Howells /* Adjust the bitmap. */
45663a4681fSDavid Howells afs_clear_contig_bits(block, slot, need_slots);
457255ed636SDavid Howells kunmap_local(block);
458255ed636SDavid Howells if (folio != folio0) {
459255ed636SDavid Howells folio_unlock(folio);
460255ed636SDavid Howells folio_put(folio);
46163a4681fSDavid Howells }
46263a4681fSDavid Howells
46363a4681fSDavid Howells /* Adjust the allocation counter. */
46463a4681fSDavid Howells if (b < AFS_DIR_BLOCKS_WITH_CTR)
46563a4681fSDavid Howells meta->meta.alloc_ctrs[b] += need_slots;
46663a4681fSDavid Howells
467874c8ca1SDavid Howells inode_set_iversion_raw(&vnode->netfs.inode, vnode->status.data_version);
46863a4681fSDavid Howells afs_stat_v(vnode, n_dir_rm);
46963a4681fSDavid Howells _debug("Remove %s from %u[%u]", name->name, b, slot);
47063a4681fSDavid Howells
47163a4681fSDavid Howells out_unmap:
472255ed636SDavid Howells kunmap_local(meta);
473255ed636SDavid Howells folio_unlock(folio0);
474255ed636SDavid Howells folio_put(folio0);
47563a4681fSDavid Howells _leave("");
47663a4681fSDavid Howells return;
47763a4681fSDavid Howells
47863a4681fSDavid Howells invalidated:
47963a4681fSDavid Howells trace_afs_edit_dir(vnode, why, afs_edit_dir_delete_inval,
48063a4681fSDavid Howells 0, 0, 0, 0, name->name);
48163a4681fSDavid Howells clear_bit(AFS_VNODE_DIR_VALID, &vnode->flags);
482255ed636SDavid Howells kunmap_local(block);
483255ed636SDavid Howells if (folio != folio0) {
484255ed636SDavid Howells folio_unlock(folio);
485255ed636SDavid Howells folio_put(folio);
48663a4681fSDavid Howells }
48763a4681fSDavid Howells goto out_unmap;
48863a4681fSDavid Howells
48963a4681fSDavid Howells error:
49063a4681fSDavid Howells trace_afs_edit_dir(vnode, why, afs_edit_dir_delete_error,
49163a4681fSDavid Howells 0, 0, 0, 0, name->name);
49263a4681fSDavid Howells clear_bit(AFS_VNODE_DIR_VALID, &vnode->flags);
49363a4681fSDavid Howells goto out_unmap;
49463a4681fSDavid Howells }
495*790dc90bSDavid Howells
496*790dc90bSDavid Howells /*
497*790dc90bSDavid Howells * Edit a subdirectory that has been moved between directories to update the
498*790dc90bSDavid Howells * ".." entry.
499*790dc90bSDavid Howells */
afs_edit_dir_update_dotdot(struct afs_vnode * vnode,struct afs_vnode * new_dvnode,enum afs_edit_dir_reason why)500*790dc90bSDavid Howells void afs_edit_dir_update_dotdot(struct afs_vnode *vnode, struct afs_vnode *new_dvnode,
501*790dc90bSDavid Howells enum afs_edit_dir_reason why)
502*790dc90bSDavid Howells {
503*790dc90bSDavid Howells union afs_xdr_dir_block *block;
504*790dc90bSDavid Howells union afs_xdr_dirent *de;
505*790dc90bSDavid Howells struct folio *folio;
506*790dc90bSDavid Howells unsigned int nr_blocks, b;
507*790dc90bSDavid Howells pgoff_t index;
508*790dc90bSDavid Howells loff_t i_size;
509*790dc90bSDavid Howells int slot;
510*790dc90bSDavid Howells
511*790dc90bSDavid Howells _enter("");
512*790dc90bSDavid Howells
513*790dc90bSDavid Howells i_size = i_size_read(&vnode->netfs.inode);
514*790dc90bSDavid Howells if (i_size < AFS_DIR_BLOCK_SIZE) {
515*790dc90bSDavid Howells clear_bit(AFS_VNODE_DIR_VALID, &vnode->flags);
516*790dc90bSDavid Howells return;
517*790dc90bSDavid Howells }
518*790dc90bSDavid Howells nr_blocks = i_size / AFS_DIR_BLOCK_SIZE;
519*790dc90bSDavid Howells
520*790dc90bSDavid Howells /* Find a block that has sufficient slots available. Each folio
521*790dc90bSDavid Howells * contains two or more directory blocks.
522*790dc90bSDavid Howells */
523*790dc90bSDavid Howells for (b = 0; b < nr_blocks; b++) {
524*790dc90bSDavid Howells index = b / AFS_DIR_BLOCKS_PER_PAGE;
525*790dc90bSDavid Howells folio = afs_dir_get_folio(vnode, index);
526*790dc90bSDavid Howells if (!folio)
527*790dc90bSDavid Howells goto error;
528*790dc90bSDavid Howells
529*790dc90bSDavid Howells block = kmap_local_folio(folio, b * AFS_DIR_BLOCK_SIZE - folio_pos(folio));
530*790dc90bSDavid Howells
531*790dc90bSDavid Howells /* Abandon the edit if we got a callback break. */
532*790dc90bSDavid Howells if (!test_bit(AFS_VNODE_DIR_VALID, &vnode->flags))
533*790dc90bSDavid Howells goto invalidated;
534*790dc90bSDavid Howells
535*790dc90bSDavid Howells slot = afs_dir_scan_block(block, &dotdot_name, b);
536*790dc90bSDavid Howells if (slot >= 0)
537*790dc90bSDavid Howells goto found_dirent;
538*790dc90bSDavid Howells
539*790dc90bSDavid Howells kunmap_local(block);
540*790dc90bSDavid Howells folio_unlock(folio);
541*790dc90bSDavid Howells folio_put(folio);
542*790dc90bSDavid Howells }
543*790dc90bSDavid Howells
544*790dc90bSDavid Howells /* Didn't find the dirent to clobber. Download the directory again. */
545*790dc90bSDavid Howells trace_afs_edit_dir(vnode, why, afs_edit_dir_update_nodd,
546*790dc90bSDavid Howells 0, 0, 0, 0, "..");
547*790dc90bSDavid Howells clear_bit(AFS_VNODE_DIR_VALID, &vnode->flags);
548*790dc90bSDavid Howells goto out;
549*790dc90bSDavid Howells
550*790dc90bSDavid Howells found_dirent:
551*790dc90bSDavid Howells de = &block->dirents[slot];
552*790dc90bSDavid Howells de->u.vnode = htonl(new_dvnode->fid.vnode);
553*790dc90bSDavid Howells de->u.unique = htonl(new_dvnode->fid.unique);
554*790dc90bSDavid Howells
555*790dc90bSDavid Howells trace_afs_edit_dir(vnode, why, afs_edit_dir_update_dd, b, slot,
556*790dc90bSDavid Howells ntohl(de->u.vnode), ntohl(de->u.unique), "..");
557*790dc90bSDavid Howells
558*790dc90bSDavid Howells kunmap_local(block);
559*790dc90bSDavid Howells folio_unlock(folio);
560*790dc90bSDavid Howells folio_put(folio);
561*790dc90bSDavid Howells inode_set_iversion_raw(&vnode->netfs.inode, vnode->status.data_version);
562*790dc90bSDavid Howells
563*790dc90bSDavid Howells out:
564*790dc90bSDavid Howells _leave("");
565*790dc90bSDavid Howells return;
566*790dc90bSDavid Howells
567*790dc90bSDavid Howells invalidated:
568*790dc90bSDavid Howells kunmap_local(block);
569*790dc90bSDavid Howells folio_unlock(folio);
570*790dc90bSDavid Howells folio_put(folio);
571*790dc90bSDavid Howells trace_afs_edit_dir(vnode, why, afs_edit_dir_update_inval,
572*790dc90bSDavid Howells 0, 0, 0, 0, "..");
573*790dc90bSDavid Howells clear_bit(AFS_VNODE_DIR_VALID, &vnode->flags);
574*790dc90bSDavid Howells goto out;
575*790dc90bSDavid Howells
576*790dc90bSDavid Howells error:
577*790dc90bSDavid Howells trace_afs_edit_dir(vnode, why, afs_edit_dir_update_error,
578*790dc90bSDavid Howells 0, 0, 0, 0, "..");
579*790dc90bSDavid Howells clear_bit(AFS_VNODE_DIR_VALID, &vnode->flags);
580*790dc90bSDavid Howells goto out;
581*790dc90bSDavid Howells }
582