1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /* Search a directory's hash table.
4 * Copyright (C) 2024 Red Hat, Inc. All Rights Reserved.
5 * Written by David Howells (dhowells@redhat.com)
7 * https://tools.ietf.org/html/draft-keiser-afs3-directory-object-00
10 #include <linux/kernel.h>
12 #include <linux/namei.h>
13 #include <linux/iversion.h>
19 * Calculate the name hash.
21 unsigned int afs_dir_hash_name(const struct qstr
*name
)
23 const unsigned char *p
= name
->name
;
24 unsigned int hash
= 0, i
;
27 for (i
= 0; i
< name
->len
; i
++)
28 hash
= (hash
* 173) + p
[i
];
29 bucket
= hash
& (AFS_DIR_HASHTBL_SIZE
- 1);
31 bucket
= AFS_DIR_HASHTBL_SIZE
- bucket
;
32 bucket
&= (AFS_DIR_HASHTBL_SIZE
- 1);
38 * Reset a directory iterator.
40 static bool afs_dir_reset_iter(struct afs_dir_iter
*iter
)
42 unsigned long long i_size
= i_size_read(&iter
->dvnode
->netfs
.inode
);
45 /* Work out the maximum number of steps we can take. */
46 nblocks
= umin(i_size
/ AFS_DIR_BLOCK_SIZE
, AFS_DIR_MAX_BLOCKS
);
49 iter
->loop_check
= nblocks
* (AFS_DIR_SLOTS_PER_BLOCK
- AFS_DIR_RESV_BLOCKS
);
50 iter
->prev_entry
= 0; /* Hash head is previous */
55 * Initialise a directory iterator for looking up a name.
57 bool afs_dir_init_iter(struct afs_dir_iter
*iter
, const struct qstr
*name
)
59 iter
->nr_slots
= afs_dir_calc_slots(name
->len
);
60 iter
->bucket
= afs_dir_hash_name(name
);
61 return afs_dir_reset_iter(iter
);
65 * Get a specific block.
67 union afs_xdr_dir_block
*afs_dir_find_block(struct afs_dir_iter
*iter
, size_t block
)
69 struct folio_queue
*fq
= iter
->fq
;
70 struct afs_vnode
*dvnode
= iter
->dvnode
;
72 size_t blpos
= block
* AFS_DIR_BLOCK_SIZE
;
73 size_t blend
= (block
+ 1) * AFS_DIR_BLOCK_SIZE
, fpos
= iter
->fpos
;
74 int slot
= iter
->fq_slot
;
76 _enter("%zx,%d", block
, slot
);
79 kunmap_local(iter
->block
);
83 if (dvnode
->directory_size
< blend
)
86 if (!fq
|| blpos
< fpos
) {
87 fq
= dvnode
->directory
;
92 /* Search the folio queue for the folio containing the block... */
93 for (; fq
; fq
= fq
->next
) {
94 for (; slot
< folioq_count(fq
); slot
++) {
95 size_t fsize
= folioq_folio_size(fq
, slot
);
97 if (blend
<= fpos
+ fsize
) {
98 /* ... and then return the mapped block. */
99 folio
= folioq_folio(fq
, slot
);
100 if (WARN_ON_ONCE(folio_pos(folio
) != fpos
))
103 iter
->fq_slot
= slot
;
105 iter
->block
= kmap_local_folio(folio
, blpos
- fpos
);
116 afs_invalidate_dir(dvnode
, afs_dir_invalid_edit_get_block
);
121 * Search through a directory bucket.
123 int afs_dir_search_bucket(struct afs_dir_iter
*iter
, const struct qstr
*name
,
124 struct afs_fid
*_fid
)
126 const union afs_xdr_dir_block
*meta
;
130 meta
= afs_dir_find_block(iter
, 0);
134 entry
= ntohs(meta
->meta
.hashtable
[iter
->bucket
& (AFS_DIR_HASHTBL_SIZE
- 1)]);
135 _enter("%x,%x", iter
->bucket
, entry
);
138 const union afs_xdr_dir_block
*block
;
139 const union afs_xdr_dirent
*dire
;
140 unsigned int blnum
= entry
/ AFS_DIR_SLOTS_PER_BLOCK
;
141 unsigned int slot
= entry
% AFS_DIR_SLOTS_PER_BLOCK
;
142 unsigned int resv
= (blnum
== 0 ? AFS_DIR_RESV_BLOCKS0
: AFS_DIR_RESV_BLOCKS
);
144 _debug("search %x", entry
);
147 kdebug("slot out of range h=%x rs=%2x sl=%2x-%2x",
148 iter
->bucket
, resv
, slot
, slot
+ iter
->nr_slots
- 1);
152 block
= afs_dir_find_block(iter
, blnum
);
155 dire
= &block
->dirents
[slot
];
157 if (slot
+ iter
->nr_slots
<= AFS_DIR_SLOTS_PER_BLOCK
&&
158 memcmp(dire
->u
.name
, name
->name
, name
->len
) == 0 &&
159 dire
->u
.name
[name
->len
] == '\0') {
160 _fid
->vnode
= ntohl(dire
->u
.vnode
);
161 _fid
->unique
= ntohl(dire
->u
.unique
);
166 iter
->prev_entry
= entry
;
167 entry
= ntohs(dire
->u
.hash_next
);
168 if (!--iter
->loop_check
) {
169 kdebug("dir chain loop h=%x", iter
->bucket
);
177 kunmap_local(iter
->block
);
183 afs_invalidate_dir(iter
->dvnode
, afs_dir_invalid_iter_stale
);
184 _leave(" = %d", ret
);
189 * Search the appropriate hash chain in the contents of an AFS directory.
191 int afs_dir_search(struct afs_vnode
*dvnode
, struct qstr
*name
,
192 struct afs_fid
*_fid
, afs_dataversion_t
*_dir_version
)
194 struct afs_dir_iter iter
= { .dvnode
= dvnode
, };
195 int ret
, retry_limit
= 3;
197 _enter("{%lu},,,", dvnode
->netfs
.inode
.i_ino
);
199 if (!afs_dir_init_iter(&iter
, name
))
202 if (--retry_limit
< 0) {
203 pr_warn("afs_read_dir(): Too many retries\n");
207 ret
= afs_read_dir(dvnode
, NULL
);
211 if (test_bit(AFS_VNODE_DELETED
, &dvnode
->flags
)) {
217 *_dir_version
= inode_peek_iversion_raw(&dvnode
->netfs
.inode
);
219 ret
= afs_dir_search_bucket(&iter
, name
, _fid
);
220 up_read(&dvnode
->validate_lock
);
222 afs_dir_reset_iter(&iter
);
223 } while (ret
== -ESTALE
);
225 _leave(" = %d", ret
);