| 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 <linux/folio_queue.h> |
| 14 | #include "internal.h" |
| 15 | #include "xdr_fs.h" |
| 16 | |
| 17 | /* |
| 18 | * Find a number of contiguous clear bits in a directory block bitmask. |
| 19 | * |
| 20 | * There are 64 slots, which means we can load the entire bitmap into a |
| 21 | * variable. The first bit doesn't count as it corresponds to the block header |
| 22 | * slot. nr_slots is between 1 and 9. |
| 23 | */ |
| 24 | static int afs_find_contig_bits(union afs_xdr_dir_block *block, unsigned int nr_slots) |
| 25 | { |
| 26 | u64 bitmap; |
| 27 | u32 mask; |
| 28 | int bit, n; |
| 29 | |
| 30 | bitmap = (u64)block->hdr.bitmap[0] << 0 * 8; |
| 31 | bitmap |= (u64)block->hdr.bitmap[1] << 1 * 8; |
| 32 | bitmap |= (u64)block->hdr.bitmap[2] << 2 * 8; |
| 33 | bitmap |= (u64)block->hdr.bitmap[3] << 3 * 8; |
| 34 | bitmap |= (u64)block->hdr.bitmap[4] << 4 * 8; |
| 35 | bitmap |= (u64)block->hdr.bitmap[5] << 5 * 8; |
| 36 | bitmap |= (u64)block->hdr.bitmap[6] << 6 * 8; |
| 37 | bitmap |= (u64)block->hdr.bitmap[7] << 7 * 8; |
| 38 | bitmap >>= 1; /* The first entry is metadata */ |
| 39 | bit = 1; |
| 40 | mask = (1 << nr_slots) - 1; |
| 41 | |
| 42 | do { |
| 43 | if (sizeof(unsigned long) == 8) |
| 44 | n = ffz(bitmap); |
| 45 | else |
| 46 | n = ((u32)bitmap) != 0 ? |
| 47 | ffz((u32)bitmap) : |
| 48 | ffz((u32)(bitmap >> 32)) + 32; |
| 49 | bitmap >>= n; |
| 50 | bit += n; |
| 51 | |
| 52 | if ((bitmap & mask) == 0) { |
| 53 | if (bit > 64 - nr_slots) |
| 54 | return -1; |
| 55 | return bit; |
| 56 | } |
| 57 | |
| 58 | n = __ffs(bitmap); |
| 59 | bitmap >>= n; |
| 60 | bit += n; |
| 61 | } while (bitmap); |
| 62 | |
| 63 | return -1; |
| 64 | } |
| 65 | |
| 66 | /* |
| 67 | * Set a number of contiguous bits in the directory block bitmap. |
| 68 | */ |
| 69 | static void afs_set_contig_bits(union afs_xdr_dir_block *block, |
| 70 | int bit, unsigned int nr_slots) |
| 71 | { |
| 72 | u64 mask; |
| 73 | |
| 74 | mask = (1 << nr_slots) - 1; |
| 75 | mask <<= bit; |
| 76 | |
| 77 | block->hdr.bitmap[0] |= (u8)(mask >> 0 * 8); |
| 78 | block->hdr.bitmap[1] |= (u8)(mask >> 1 * 8); |
| 79 | block->hdr.bitmap[2] |= (u8)(mask >> 2 * 8); |
| 80 | block->hdr.bitmap[3] |= (u8)(mask >> 3 * 8); |
| 81 | block->hdr.bitmap[4] |= (u8)(mask >> 4 * 8); |
| 82 | block->hdr.bitmap[5] |= (u8)(mask >> 5 * 8); |
| 83 | block->hdr.bitmap[6] |= (u8)(mask >> 6 * 8); |
| 84 | block->hdr.bitmap[7] |= (u8)(mask >> 7 * 8); |
| 85 | } |
| 86 | |
| 87 | /* |
| 88 | * Clear a number of contiguous bits in the directory block bitmap. |
| 89 | */ |
| 90 | static void afs_clear_contig_bits(union afs_xdr_dir_block *block, |
| 91 | int bit, unsigned int nr_slots) |
| 92 | { |
| 93 | u64 mask; |
| 94 | |
| 95 | mask = (1 << nr_slots) - 1; |
| 96 | mask <<= bit; |
| 97 | |
| 98 | block->hdr.bitmap[0] &= ~(u8)(mask >> 0 * 8); |
| 99 | block->hdr.bitmap[1] &= ~(u8)(mask >> 1 * 8); |
| 100 | block->hdr.bitmap[2] &= ~(u8)(mask >> 2 * 8); |
| 101 | block->hdr.bitmap[3] &= ~(u8)(mask >> 3 * 8); |
| 102 | block->hdr.bitmap[4] &= ~(u8)(mask >> 4 * 8); |
| 103 | block->hdr.bitmap[5] &= ~(u8)(mask >> 5 * 8); |
| 104 | block->hdr.bitmap[6] &= ~(u8)(mask >> 6 * 8); |
| 105 | block->hdr.bitmap[7] &= ~(u8)(mask >> 7 * 8); |
| 106 | } |
| 107 | |
| 108 | /* |
| 109 | * Get a specific block, extending the directory storage to cover it as needed. |
| 110 | */ |
| 111 | static union afs_xdr_dir_block *afs_dir_get_block(struct afs_dir_iter *iter, size_t block) |
| 112 | { |
| 113 | struct folio_queue *fq; |
| 114 | struct afs_vnode *dvnode = iter->dvnode; |
| 115 | struct folio *folio; |
| 116 | size_t blpos = block * AFS_DIR_BLOCK_SIZE; |
| 117 | size_t blend = (block + 1) * AFS_DIR_BLOCK_SIZE, fpos = iter->fpos; |
| 118 | int ret; |
| 119 | |
| 120 | if (dvnode->directory_size < blend) { |
| 121 | size_t cur_size = dvnode->directory_size; |
| 122 | |
| 123 | ret = netfs_alloc_folioq_buffer( |
| 124 | NULL, buffer: &dvnode->directory, cur_size: &cur_size, size: blend, |
| 125 | gfp: mapping_gfp_mask(mapping: dvnode->netfs.inode.i_mapping)); |
| 126 | dvnode->directory_size = cur_size; |
| 127 | if (ret < 0) |
| 128 | goto fail; |
| 129 | } |
| 130 | |
| 131 | fq = iter->fq; |
| 132 | if (!fq) |
| 133 | fq = dvnode->directory; |
| 134 | |
| 135 | /* Search the folio queue for the folio containing the block... */ |
| 136 | for (; fq; fq = fq->next) { |
| 137 | for (int s = iter->fq_slot; s < folioq_count(folioq: fq); s++) { |
| 138 | size_t fsize = folioq_folio_size(folioq: fq, slot: s); |
| 139 | |
| 140 | if (blend <= fpos + fsize) { |
| 141 | /* ... and then return the mapped block. */ |
| 142 | folio = folioq_folio(folioq: fq, slot: s); |
| 143 | if (WARN_ON_ONCE(folio_pos(folio) != fpos)) |
| 144 | goto fail; |
| 145 | iter->fq = fq; |
| 146 | iter->fq_slot = s; |
| 147 | iter->fpos = fpos; |
| 148 | return kmap_local_folio(folio, offset: blpos - fpos); |
| 149 | } |
| 150 | fpos += fsize; |
| 151 | } |
| 152 | iter->fq_slot = 0; |
| 153 | } |
| 154 | |
| 155 | fail: |
| 156 | iter->fq = NULL; |
| 157 | iter->fq_slot = 0; |
| 158 | afs_invalidate_dir(dvnode, trace: afs_dir_invalid_edit_get_block); |
| 159 | return NULL; |
| 160 | } |
| 161 | |
| 162 | /* |
| 163 | * Scan a directory block looking for a dirent of the right name. |
| 164 | */ |
| 165 | static int afs_dir_scan_block(const union afs_xdr_dir_block *block, const struct qstr *name, |
| 166 | unsigned int blocknum) |
| 167 | { |
| 168 | const union afs_xdr_dirent *de; |
| 169 | u64 bitmap; |
| 170 | int d, len, n; |
| 171 | |
| 172 | _enter("" ); |
| 173 | |
| 174 | bitmap = (u64)block->hdr.bitmap[0] << 0 * 8; |
| 175 | bitmap |= (u64)block->hdr.bitmap[1] << 1 * 8; |
| 176 | bitmap |= (u64)block->hdr.bitmap[2] << 2 * 8; |
| 177 | bitmap |= (u64)block->hdr.bitmap[3] << 3 * 8; |
| 178 | bitmap |= (u64)block->hdr.bitmap[4] << 4 * 8; |
| 179 | bitmap |= (u64)block->hdr.bitmap[5] << 5 * 8; |
| 180 | bitmap |= (u64)block->hdr.bitmap[6] << 6 * 8; |
| 181 | bitmap |= (u64)block->hdr.bitmap[7] << 7 * 8; |
| 182 | |
| 183 | for (d = (blocknum == 0 ? AFS_DIR_RESV_BLOCKS0 : AFS_DIR_RESV_BLOCKS); |
| 184 | d < AFS_DIR_SLOTS_PER_BLOCK; |
| 185 | d++) { |
| 186 | if (!((bitmap >> d) & 1)) |
| 187 | continue; |
| 188 | de = &block->dirents[d]; |
| 189 | if (de->u.valid != 1) |
| 190 | continue; |
| 191 | |
| 192 | /* The block was NUL-terminated by afs_dir_check_page(). */ |
| 193 | len = strlen(de->u.name); |
| 194 | if (len == name->len && |
| 195 | memcmp(p: de->u.name, q: name->name, size: name->len) == 0) |
| 196 | return d; |
| 197 | |
| 198 | n = round_up(12 + len + 1 + 4, AFS_DIR_DIRENT_SIZE); |
| 199 | n /= AFS_DIR_DIRENT_SIZE; |
| 200 | d += n - 1; |
| 201 | } |
| 202 | |
| 203 | return -1; |
| 204 | } |
| 205 | |
| 206 | /* |
| 207 | * Initialise a new directory block. Note that block 0 is special and contains |
| 208 | * some extra metadata. |
| 209 | */ |
| 210 | static void afs_edit_init_block(union afs_xdr_dir_block *meta, |
| 211 | union afs_xdr_dir_block *block, int block_num) |
| 212 | { |
| 213 | memset(block, 0, sizeof(*block)); |
| 214 | block->hdr.npages = htons(1); |
| 215 | block->hdr.magic = AFS_DIR_MAGIC; |
| 216 | block->hdr.bitmap[0] = 1; |
| 217 | |
| 218 | if (block_num == 0) { |
| 219 | block->hdr.bitmap[0] = 0xff; |
| 220 | block->hdr.bitmap[1] = 0x1f; |
| 221 | memset(block->meta.alloc_ctrs, |
| 222 | AFS_DIR_SLOTS_PER_BLOCK, |
| 223 | sizeof(block->meta.alloc_ctrs)); |
| 224 | meta->meta.alloc_ctrs[0] = |
| 225 | AFS_DIR_SLOTS_PER_BLOCK - AFS_DIR_RESV_BLOCKS0; |
| 226 | } |
| 227 | |
| 228 | if (block_num < AFS_DIR_BLOCKS_WITH_CTR) |
| 229 | meta->meta.alloc_ctrs[block_num] = |
| 230 | AFS_DIR_SLOTS_PER_BLOCK - AFS_DIR_RESV_BLOCKS; |
| 231 | } |
| 232 | |
| 233 | /* |
| 234 | * Edit a directory's file data to add a new directory entry. Doing this after |
| 235 | * create, mkdir, symlink, link or rename if the data version number is |
| 236 | * incremented by exactly one avoids the need to re-download the entire |
| 237 | * directory contents. |
| 238 | * |
| 239 | * The caller must hold the inode locked. |
| 240 | */ |
| 241 | void afs_edit_dir_add(struct afs_vnode *vnode, |
| 242 | const struct qstr *name, struct afs_fid *new_fid, |
| 243 | enum afs_edit_dir_reason why) |
| 244 | { |
| 245 | union afs_xdr_dir_block *meta, *block; |
| 246 | union afs_xdr_dirent *de; |
| 247 | struct afs_dir_iter iter = { .dvnode = vnode }; |
| 248 | unsigned int nr_blocks, b, entry; |
| 249 | loff_t i_size; |
| 250 | int slot; |
| 251 | |
| 252 | _enter(",,{%d,%s}," , name->len, name->name); |
| 253 | |
| 254 | i_size = i_size_read(inode: &vnode->netfs.inode); |
| 255 | if (i_size > AFS_DIR_BLOCK_SIZE * AFS_DIR_MAX_BLOCKS || |
| 256 | (i_size & (AFS_DIR_BLOCK_SIZE - 1))) { |
| 257 | afs_invalidate_dir(dvnode: vnode, trace: afs_dir_invalid_edit_add_bad_size); |
| 258 | return; |
| 259 | } |
| 260 | |
| 261 | meta = afs_dir_get_block(iter: &iter, block: 0); |
| 262 | if (!meta) |
| 263 | return; |
| 264 | |
| 265 | /* Work out how many slots we're going to need. */ |
| 266 | iter.nr_slots = afs_dir_calc_slots(name_len: name->len); |
| 267 | |
| 268 | if (i_size == 0) |
| 269 | goto new_directory; |
| 270 | nr_blocks = i_size / AFS_DIR_BLOCK_SIZE; |
| 271 | |
| 272 | /* Find a block that has sufficient slots available. Each folio |
| 273 | * contains two or more directory blocks. |
| 274 | */ |
| 275 | for (b = 0; b < nr_blocks + 1; b++) { |
| 276 | /* If the directory extended into a new folio, then we need to |
| 277 | * tack a new folio on the end. |
| 278 | */ |
| 279 | if (nr_blocks >= AFS_DIR_MAX_BLOCKS) |
| 280 | goto error_too_many_blocks; |
| 281 | |
| 282 | /* Lower dir blocks have a counter in the header we can check. */ |
| 283 | if (b < AFS_DIR_BLOCKS_WITH_CTR && |
| 284 | meta->meta.alloc_ctrs[b] < iter.nr_slots) |
| 285 | continue; |
| 286 | |
| 287 | block = afs_dir_get_block(iter: &iter, block: b); |
| 288 | if (!block) |
| 289 | goto error; |
| 290 | |
| 291 | /* Abandon the edit if we got a callback break. */ |
| 292 | if (!test_bit(AFS_VNODE_DIR_VALID, &vnode->flags)) |
| 293 | goto already_invalidated; |
| 294 | |
| 295 | _debug("block %u: %2u %3u %u" , |
| 296 | b, |
| 297 | (b < AFS_DIR_BLOCKS_WITH_CTR) ? meta->meta.alloc_ctrs[b] : 99, |
| 298 | ntohs(block->hdr.npages), |
| 299 | ntohs(block->hdr.magic)); |
| 300 | |
| 301 | /* Initialise the block if necessary. */ |
| 302 | if (b == nr_blocks) { |
| 303 | _debug("init %u" , b); |
| 304 | afs_edit_init_block(meta, block, block_num: b); |
| 305 | afs_set_i_size(vnode, size: (b + 1) * AFS_DIR_BLOCK_SIZE); |
| 306 | } |
| 307 | |
| 308 | /* We need to try and find one or more consecutive slots to |
| 309 | * hold the entry. |
| 310 | */ |
| 311 | slot = afs_find_contig_bits(block, nr_slots: iter.nr_slots); |
| 312 | if (slot >= 0) { |
| 313 | _debug("slot %u" , slot); |
| 314 | goto found_space; |
| 315 | } |
| 316 | |
| 317 | kunmap_local(block); |
| 318 | } |
| 319 | |
| 320 | /* There are no spare slots of sufficient size, yet the operation |
| 321 | * succeeded. Download the directory again. |
| 322 | */ |
| 323 | trace_afs_edit_dir(dvnode: vnode, why, op: afs_edit_dir_create_nospc, block: 0, slot: 0, f_vnode: 0, f_unique: 0, name: name->name); |
| 324 | afs_invalidate_dir(dvnode: vnode, trace: afs_dir_invalid_edit_add_no_slots); |
| 325 | goto out_unmap; |
| 326 | |
| 327 | new_directory: |
| 328 | afs_edit_init_block(meta, block: meta, block_num: 0); |
| 329 | i_size = AFS_DIR_BLOCK_SIZE; |
| 330 | afs_set_i_size(vnode, size: i_size); |
| 331 | slot = AFS_DIR_RESV_BLOCKS0; |
| 332 | block = afs_dir_get_block(iter: &iter, block: 0); |
| 333 | nr_blocks = 1; |
| 334 | b = 0; |
| 335 | |
| 336 | found_space: |
| 337 | /* Set the dirent slot. */ |
| 338 | trace_afs_edit_dir(dvnode: vnode, why, op: afs_edit_dir_create, block: b, slot, |
| 339 | f_vnode: new_fid->vnode, f_unique: new_fid->unique, name: name->name); |
| 340 | de = &block->dirents[slot]; |
| 341 | de->u.valid = 1; |
| 342 | de->u.unused[0] = 0; |
| 343 | de->u.hash_next = 0; // TODO: Really need to maintain this |
| 344 | de->u.vnode = htonl(new_fid->vnode); |
| 345 | de->u.unique = htonl(new_fid->unique); |
| 346 | memcpy(de->u.name, name->name, name->len + 1); |
| 347 | de->u.name[name->len] = 0; |
| 348 | |
| 349 | /* Adjust the bitmap. */ |
| 350 | afs_set_contig_bits(block, bit: slot, nr_slots: iter.nr_slots); |
| 351 | |
| 352 | /* Adjust the allocation counter. */ |
| 353 | if (b < AFS_DIR_BLOCKS_WITH_CTR) |
| 354 | meta->meta.alloc_ctrs[b] -= iter.nr_slots; |
| 355 | |
| 356 | /* Adjust the hash chain. */ |
| 357 | entry = b * AFS_DIR_SLOTS_PER_BLOCK + slot; |
| 358 | iter.bucket = afs_dir_hash_name(name); |
| 359 | de->u.hash_next = meta->meta.hashtable[iter.bucket]; |
| 360 | meta->meta.hashtable[iter.bucket] = htons(entry); |
| 361 | kunmap_local(block); |
| 362 | |
| 363 | inode_inc_iversion_raw(inode: &vnode->netfs.inode); |
| 364 | afs_stat_v(vnode, n_dir_cr); |
| 365 | _debug("Insert %s in %u[%u]" , name->name, b, slot); |
| 366 | |
| 367 | netfs_single_mark_inode_dirty(inode: &vnode->netfs.inode); |
| 368 | |
| 369 | out_unmap: |
| 370 | kunmap_local(meta); |
| 371 | _leave("" ); |
| 372 | return; |
| 373 | |
| 374 | already_invalidated: |
| 375 | trace_afs_edit_dir(dvnode: vnode, why, op: afs_edit_dir_create_inval, block: 0, slot: 0, f_vnode: 0, f_unique: 0, name: name->name); |
| 376 | kunmap_local(block); |
| 377 | goto out_unmap; |
| 378 | |
| 379 | error_too_many_blocks: |
| 380 | afs_invalidate_dir(dvnode: vnode, trace: afs_dir_invalid_edit_add_too_many_blocks); |
| 381 | error: |
| 382 | trace_afs_edit_dir(dvnode: vnode, why, op: afs_edit_dir_create_error, block: 0, slot: 0, f_vnode: 0, f_unique: 0, name: name->name); |
| 383 | goto out_unmap; |
| 384 | } |
| 385 | |
| 386 | /* |
| 387 | * Edit a directory's file data to remove a new directory entry. Doing this |
| 388 | * after unlink, rmdir or rename if the data version number is incremented by |
| 389 | * exactly one avoids the need to re-download the entire directory contents. |
| 390 | * |
| 391 | * The caller must hold the inode locked. |
| 392 | */ |
| 393 | void afs_edit_dir_remove(struct afs_vnode *vnode, |
| 394 | const struct qstr *name, enum afs_edit_dir_reason why) |
| 395 | { |
| 396 | union afs_xdr_dir_block *meta, *block, *pblock; |
| 397 | union afs_xdr_dirent *de, *pde; |
| 398 | struct afs_dir_iter iter = { .dvnode = vnode }; |
| 399 | struct afs_fid fid; |
| 400 | unsigned int b, slot, entry; |
| 401 | loff_t i_size; |
| 402 | __be16 next; |
| 403 | int found; |
| 404 | |
| 405 | _enter(",,{%d,%s}," , name->len, name->name); |
| 406 | |
| 407 | i_size = i_size_read(inode: &vnode->netfs.inode); |
| 408 | if (i_size < AFS_DIR_BLOCK_SIZE || |
| 409 | i_size > AFS_DIR_BLOCK_SIZE * AFS_DIR_MAX_BLOCKS || |
| 410 | (i_size & (AFS_DIR_BLOCK_SIZE - 1))) { |
| 411 | afs_invalidate_dir(dvnode: vnode, trace: afs_dir_invalid_edit_rem_bad_size); |
| 412 | return; |
| 413 | } |
| 414 | |
| 415 | if (!afs_dir_init_iter(iter: &iter, name)) |
| 416 | return; |
| 417 | |
| 418 | meta = afs_dir_find_block(iter: &iter, block: 0); |
| 419 | if (!meta) |
| 420 | return; |
| 421 | |
| 422 | /* Find the entry in the blob. */ |
| 423 | found = afs_dir_search_bucket(iter: &iter, name, fid: &fid); |
| 424 | if (found < 0) { |
| 425 | /* Didn't find the dirent to clobber. Re-download. */ |
| 426 | trace_afs_edit_dir(dvnode: vnode, why, op: afs_edit_dir_delete_noent, |
| 427 | block: 0, slot: 0, f_vnode: 0, f_unique: 0, name: name->name); |
| 428 | afs_invalidate_dir(dvnode: vnode, trace: afs_dir_invalid_edit_rem_wrong_name); |
| 429 | goto out_unmap; |
| 430 | } |
| 431 | |
| 432 | entry = found; |
| 433 | b = entry / AFS_DIR_SLOTS_PER_BLOCK; |
| 434 | slot = entry % AFS_DIR_SLOTS_PER_BLOCK; |
| 435 | |
| 436 | block = afs_dir_find_block(iter: &iter, block: b); |
| 437 | if (!block) |
| 438 | goto error; |
| 439 | if (!test_bit(AFS_VNODE_DIR_VALID, &vnode->flags)) |
| 440 | goto already_invalidated; |
| 441 | |
| 442 | /* Check and clear the entry. */ |
| 443 | de = &block->dirents[slot]; |
| 444 | if (de->u.valid != 1) |
| 445 | goto error_unmap; |
| 446 | |
| 447 | trace_afs_edit_dir(dvnode: vnode, why, op: afs_edit_dir_delete, block: b, slot, |
| 448 | ntohl(de->u.vnode), ntohl(de->u.unique), |
| 449 | name: name->name); |
| 450 | |
| 451 | /* Adjust the bitmap. */ |
| 452 | afs_clear_contig_bits(block, bit: slot, nr_slots: iter.nr_slots); |
| 453 | |
| 454 | /* Adjust the allocation counter. */ |
| 455 | if (b < AFS_DIR_BLOCKS_WITH_CTR) |
| 456 | meta->meta.alloc_ctrs[b] += iter.nr_slots; |
| 457 | |
| 458 | /* Clear the constituent entries. */ |
| 459 | next = de->u.hash_next; |
| 460 | memset(de, 0, sizeof(*de) * iter.nr_slots); |
| 461 | kunmap_local(block); |
| 462 | |
| 463 | /* Adjust the hash chain: if iter->prev_entry is 0, the hashtable head |
| 464 | * index is previous; otherwise it's slot number of the previous entry. |
| 465 | */ |
| 466 | if (!iter.prev_entry) { |
| 467 | __be16 prev_next = meta->meta.hashtable[iter.bucket]; |
| 468 | |
| 469 | if (unlikely(prev_next != htons(entry))) { |
| 470 | pr_warn("%llx:%llx:%x: not head of chain b=%x p=%x,%x e=%x %*s" , |
| 471 | vnode->fid.vid, vnode->fid.vnode, vnode->fid.unique, |
| 472 | iter.bucket, iter.prev_entry, prev_next, entry, |
| 473 | name->len, name->name); |
| 474 | goto error; |
| 475 | } |
| 476 | meta->meta.hashtable[iter.bucket] = next; |
| 477 | } else { |
| 478 | unsigned int pb = iter.prev_entry / AFS_DIR_SLOTS_PER_BLOCK; |
| 479 | unsigned int ps = iter.prev_entry % AFS_DIR_SLOTS_PER_BLOCK; |
| 480 | __be16 prev_next; |
| 481 | |
| 482 | pblock = afs_dir_find_block(iter: &iter, block: pb); |
| 483 | if (!pblock) |
| 484 | goto error; |
| 485 | pde = &pblock->dirents[ps]; |
| 486 | prev_next = pde->u.hash_next; |
| 487 | if (prev_next != htons(entry)) { |
| 488 | kunmap_local(pblock); |
| 489 | pr_warn("%llx:%llx:%x: not prev in chain b=%x p=%x,%x e=%x %*s" , |
| 490 | vnode->fid.vid, vnode->fid.vnode, vnode->fid.unique, |
| 491 | iter.bucket, iter.prev_entry, prev_next, entry, |
| 492 | name->len, name->name); |
| 493 | goto error; |
| 494 | } |
| 495 | pde->u.hash_next = next; |
| 496 | kunmap_local(pblock); |
| 497 | } |
| 498 | |
| 499 | netfs_single_mark_inode_dirty(inode: &vnode->netfs.inode); |
| 500 | |
| 501 | inode_set_iversion_raw(inode: &vnode->netfs.inode, val: vnode->status.data_version); |
| 502 | afs_stat_v(vnode, n_dir_rm); |
| 503 | _debug("Remove %s from %u[%u]" , name->name, b, slot); |
| 504 | |
| 505 | out_unmap: |
| 506 | kunmap_local(meta); |
| 507 | _leave("" ); |
| 508 | return; |
| 509 | |
| 510 | already_invalidated: |
| 511 | kunmap_local(block); |
| 512 | trace_afs_edit_dir(dvnode: vnode, why, op: afs_edit_dir_delete_inval, |
| 513 | block: 0, slot: 0, f_vnode: 0, f_unique: 0, name: name->name); |
| 514 | goto out_unmap; |
| 515 | |
| 516 | error_unmap: |
| 517 | kunmap_local(block); |
| 518 | error: |
| 519 | trace_afs_edit_dir(dvnode: vnode, why, op: afs_edit_dir_delete_error, |
| 520 | block: 0, slot: 0, f_vnode: 0, f_unique: 0, name: name->name); |
| 521 | goto out_unmap; |
| 522 | } |
| 523 | |
| 524 | /* |
| 525 | * Edit an entry in a directory to update the vnode it refers to. This is also |
| 526 | * used to update the ".." entry in a directory. |
| 527 | */ |
| 528 | void afs_edit_dir_update(struct afs_vnode *vnode, const struct qstr *name, |
| 529 | struct afs_vnode *new_dvnode, enum afs_edit_dir_reason why) |
| 530 | { |
| 531 | union afs_xdr_dir_block *block; |
| 532 | union afs_xdr_dirent *de; |
| 533 | struct afs_dir_iter iter = { .dvnode = vnode }; |
| 534 | unsigned int nr_blocks, b; |
| 535 | loff_t i_size; |
| 536 | int slot; |
| 537 | |
| 538 | _enter("" ); |
| 539 | |
| 540 | i_size = i_size_read(inode: &vnode->netfs.inode); |
| 541 | if (i_size < AFS_DIR_BLOCK_SIZE) { |
| 542 | afs_invalidate_dir(dvnode: vnode, trace: afs_dir_invalid_edit_upd_bad_size); |
| 543 | return; |
| 544 | } |
| 545 | |
| 546 | nr_blocks = i_size / AFS_DIR_BLOCK_SIZE; |
| 547 | |
| 548 | /* Find a block that has sufficient slots available. Each folio |
| 549 | * contains two or more directory blocks. |
| 550 | */ |
| 551 | for (b = 0; b < nr_blocks; b++) { |
| 552 | block = afs_dir_get_block(iter: &iter, block: b); |
| 553 | if (!block) |
| 554 | goto error; |
| 555 | |
| 556 | /* Abandon the edit if we got a callback break. */ |
| 557 | if (!test_bit(AFS_VNODE_DIR_VALID, &vnode->flags)) |
| 558 | goto already_invalidated; |
| 559 | |
| 560 | slot = afs_dir_scan_block(block, name, blocknum: b); |
| 561 | if (slot >= 0) |
| 562 | goto found_dirent; |
| 563 | |
| 564 | kunmap_local(block); |
| 565 | } |
| 566 | |
| 567 | /* Didn't find the dirent to clobber. Download the directory again. */ |
| 568 | trace_afs_edit_dir(dvnode: vnode, why, op: afs_edit_dir_update_nodd, |
| 569 | block: 0, slot: 0, f_vnode: 0, f_unique: 0, name: name->name); |
| 570 | afs_invalidate_dir(dvnode: vnode, trace: afs_dir_invalid_edit_upd_no_dd); |
| 571 | goto out; |
| 572 | |
| 573 | found_dirent: |
| 574 | de = &block->dirents[slot]; |
| 575 | de->u.vnode = htonl(new_dvnode->fid.vnode); |
| 576 | de->u.unique = htonl(new_dvnode->fid.unique); |
| 577 | |
| 578 | trace_afs_edit_dir(dvnode: vnode, why, op: afs_edit_dir_update_dd, block: b, slot, |
| 579 | ntohl(de->u.vnode), ntohl(de->u.unique), name: name->name); |
| 580 | |
| 581 | kunmap_local(block); |
| 582 | netfs_single_mark_inode_dirty(inode: &vnode->netfs.inode); |
| 583 | inode_set_iversion_raw(inode: &vnode->netfs.inode, val: vnode->status.data_version); |
| 584 | |
| 585 | out: |
| 586 | _leave("" ); |
| 587 | return; |
| 588 | |
| 589 | already_invalidated: |
| 590 | kunmap_local(block); |
| 591 | trace_afs_edit_dir(dvnode: vnode, why, op: afs_edit_dir_update_inval, |
| 592 | block: 0, slot: 0, f_vnode: 0, f_unique: 0, name: name->name); |
| 593 | goto out; |
| 594 | |
| 595 | error: |
| 596 | trace_afs_edit_dir(dvnode: vnode, why, op: afs_edit_dir_update_error, |
| 597 | block: 0, slot: 0, f_vnode: 0, f_unique: 0, name: name->name); |
| 598 | goto out; |
| 599 | } |
| 600 | |
| 601 | /* |
| 602 | * Initialise a new directory. We need to fill in the "." and ".." entries. |
| 603 | */ |
| 604 | void afs_mkdir_init_dir(struct afs_vnode *dvnode, struct afs_vnode *parent_dvnode) |
| 605 | { |
| 606 | union afs_xdr_dir_block *meta; |
| 607 | struct afs_dir_iter iter = { .dvnode = dvnode }; |
| 608 | union afs_xdr_dirent *de; |
| 609 | unsigned int slot = AFS_DIR_RESV_BLOCKS0; |
| 610 | loff_t i_size; |
| 611 | |
| 612 | i_size = i_size_read(inode: &dvnode->netfs.inode); |
| 613 | if (i_size != AFS_DIR_BLOCK_SIZE) { |
| 614 | afs_invalidate_dir(dvnode, trace: afs_dir_invalid_edit_add_bad_size); |
| 615 | return; |
| 616 | } |
| 617 | |
| 618 | meta = afs_dir_get_block(iter: &iter, block: 0); |
| 619 | if (!meta) |
| 620 | return; |
| 621 | |
| 622 | afs_edit_init_block(meta, block: meta, block_num: 0); |
| 623 | |
| 624 | de = &meta->dirents[slot]; |
| 625 | de->u.valid = 1; |
| 626 | de->u.vnode = htonl(dvnode->fid.vnode); |
| 627 | de->u.unique = htonl(dvnode->fid.unique); |
| 628 | memcpy(de->u.name, "." , 2); |
| 629 | trace_afs_edit_dir(dvnode, why: afs_edit_dir_for_mkdir, op: afs_edit_dir_mkdir, block: 0, slot, |
| 630 | f_vnode: dvnode->fid.vnode, f_unique: dvnode->fid.unique, name: "." ); |
| 631 | slot++; |
| 632 | |
| 633 | de = &meta->dirents[slot]; |
| 634 | de->u.valid = 1; |
| 635 | de->u.vnode = htonl(parent_dvnode->fid.vnode); |
| 636 | de->u.unique = htonl(parent_dvnode->fid.unique); |
| 637 | memcpy(de->u.name, ".." , 3); |
| 638 | trace_afs_edit_dir(dvnode, why: afs_edit_dir_for_mkdir, op: afs_edit_dir_mkdir, block: 0, slot, |
| 639 | f_vnode: parent_dvnode->fid.vnode, f_unique: parent_dvnode->fid.unique, name: ".." ); |
| 640 | |
| 641 | afs_set_contig_bits(block: meta, AFS_DIR_RESV_BLOCKS0, nr_slots: 2); |
| 642 | meta->meta.alloc_ctrs[0] -= 2; |
| 643 | kunmap_local(meta); |
| 644 | |
| 645 | netfs_single_mark_inode_dirty(inode: &dvnode->netfs.inode); |
| 646 | set_bit(AFS_VNODE_DIR_VALID, addr: &dvnode->flags); |
| 647 | set_bit(AFS_VNODE_DIR_READ, addr: &dvnode->flags); |
| 648 | } |
| 649 | |