ext2fs_htree.c revision 1.2 1 1.2 christos /* $NetBSD: ext2fs_htree.c,v 1.2 2016/08/09 07:15:35 christos Exp $ */
2 1.1 christos
3 1.1 christos /*-
4 1.1 christos * Copyright (c) 2010, 2012 Zheng Liu <lz (at) freebsd.org>
5 1.1 christos * Copyright (c) 2012, Vyacheslav Matyushin
6 1.1 christos * All rights reserved.
7 1.1 christos *
8 1.1 christos * Redistribution and use in source and binary forms, with or without
9 1.1 christos * modification, are permitted provided that the following conditions
10 1.1 christos * are met:
11 1.1 christos * 1. Redistributions of source code must retain the above copyright
12 1.1 christos * notice, this list of conditions and the following disclaimer.
13 1.1 christos * 2. Redistributions in binary form must reproduce the above copyright
14 1.1 christos * notice, this list of conditions and the following disclaimer in the
15 1.1 christos * documentation and/or other materials provided with the distribution.
16 1.1 christos *
17 1.1 christos * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18 1.1 christos * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 1.1 christos * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 1.1 christos * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21 1.1 christos * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 1.1 christos * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 1.1 christos * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 1.1 christos * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 1.1 christos * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 1.1 christos * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 1.1 christos * SUCH DAMAGE.
28 1.1 christos *
29 1.2 christos * $FreeBSD: head/sys/fs/ext2fs/ext2fs_htree.c 294653 2016-01-24 02:41:49Z pfg $
30 1.1 christos */
31 1.1 christos #include <sys/cdefs.h>
32 1.2 christos __KERNEL_RCSID(0, "$NetBSD: ext2fs_htree.c,v 1.2 2016/08/09 07:15:35 christos Exp $");
33 1.1 christos
34 1.1 christos #include <sys/param.h>
35 1.1 christos #include <sys/systm.h>
36 1.1 christos #include <sys/resourcevar.h>
37 1.1 christos #include <sys/kernel.h>
38 1.1 christos #include <sys/file.h>
39 1.1 christos #include <sys/stat.h>
40 1.1 christos #include <sys/buf.h>
41 1.1 christos #include <sys/proc.h>
42 1.1 christos #include <sys/mount.h>
43 1.1 christos #include <sys/namei.h>
44 1.1 christos #include <sys/vnode.h>
45 1.1 christos #include <sys/lockf.h>
46 1.1 christos #include <sys/pool.h>
47 1.1 christos #include <sys/signalvar.h>
48 1.1 christos #include <sys/kauth.h>
49 1.2 christos #include <sys/malloc.h>
50 1.1 christos #include <ufs/ufs/dir.h>
51 1.1 christos
52 1.1 christos #include <ufs/ufs/inode.h>
53 1.1 christos #include <ufs/ufs/ufsmount.h>
54 1.1 christos #include <ufs/ext2fs/ext2fs.h>
55 1.1 christos
56 1.1 christos #include <ufs/ext2fs/ext2fs_extern.h>
57 1.1 christos #include <ufs/ext2fs/ext2fs_dinode.h>
58 1.1 christos #include <ufs/ext2fs/ext2fs_dir.h>
59 1.1 christos #include <ufs/ext2fs/ext2fs_htree.h>
60 1.1 christos #include <ufs/ext2fs/ext2fs_hash.h>
61 1.1 christos
62 1.2 christos static int ext2fs_htree_find_leaf(struct inode *, const char *, int ,
63 1.2 christos uint32_t *, uint8_t *, struct ext2fs_htree_lookup_info *);
64 1.2 christos
65 1.1 christos int
66 1.1 christos ext2fs_htree_has_idx(struct inode *ip)
67 1.1 christos {
68 1.2 christos /* XXX ip->i_flags should have got checked here for IN_E3INDEX */
69 1.1 christos return EXT2_HAS_COMPAT_FEATURE(ip->i_e2fs, EXT2F_COMPAT_DIRHASHINDEX)
70 1.1 christos && (ip->i_din.e2fs_din->e2di_flags & EXT2_INDEX);
71 1.1 christos }
72 1.1 christos
73 1.1 christos static off_t
74 1.1 christos ext2fs_htree_get_block(struct ext2fs_htree_entry *ep)
75 1.1 christos {
76 1.1 christos return ep->h_blk & 0x00FFFFFF;
77 1.1 christos }
78 1.1 christos
79 1.1 christos static void
80 1.1 christos ext2fs_htree_release(struct ext2fs_htree_lookup_info *info)
81 1.1 christos {
82 1.1 christos for (u_int i = 0; i < info->h_levels_num; i++) {
83 1.1 christos struct buf *bp = info->h_levels[i].h_bp;
84 1.1 christos if (bp != NULL)
85 1.1 christos brelse(bp, 0);
86 1.1 christos }
87 1.1 christos }
88 1.1 christos
89 1.1 christos static uint16_t
90 1.1 christos ext2fs_htree_get_limit(struct ext2fs_htree_entry *ep)
91 1.1 christos {
92 1.1 christos return ((struct ext2fs_htree_count *)(ep))->h_entries_max;
93 1.1 christos }
94 1.1 christos
95 1.1 christos static uint32_t
96 1.1 christos ext2fs_htree_root_limit(struct inode *ip, int len)
97 1.1 christos {
98 1.1 christos uint32_t space = ip->i_e2fs->e2fs_bsize - EXT2_DIR_REC_LEN(1) -
99 1.1 christos EXT2_DIR_REC_LEN(2) - len;
100 1.1 christos return space / sizeof(struct ext2fs_htree_entry);
101 1.1 christos }
102 1.1 christos
103 1.1 christos static uint16_t
104 1.1 christos ext2fs_htree_get_count(struct ext2fs_htree_entry *ep)
105 1.1 christos {
106 1.1 christos return ((struct ext2fs_htree_count *)(ep))->h_entries_num;
107 1.1 christos }
108 1.1 christos
109 1.1 christos static uint32_t
110 1.1 christos ext2fs_htree_get_hash(struct ext2fs_htree_entry *ep)
111 1.1 christos {
112 1.1 christos return ep->h_hash;
113 1.1 christos }
114 1.1 christos
115 1.2 christos
116 1.2 christos static void
117 1.2 christos ext2fs_htree_set_block(struct ext2fs_htree_entry *ep, uint32_t blk)
118 1.2 christos {
119 1.2 christos ep->h_blk = blk;
120 1.2 christos }
121 1.2 christos
122 1.2 christos static void
123 1.2 christos ext2fs_htree_set_count(struct ext2fs_htree_entry *ep, uint16_t cnt)
124 1.2 christos {
125 1.2 christos ((struct ext2fs_htree_count *)(ep))->h_entries_num = cnt;
126 1.2 christos }
127 1.2 christos
128 1.2 christos static void
129 1.2 christos ext2fs_htree_set_hash(struct ext2fs_htree_entry *ep, uint32_t hash)
130 1.2 christos {
131 1.2 christos ep->h_hash = hash;
132 1.2 christos }
133 1.2 christos
134 1.2 christos static void
135 1.2 christos ext2fs_htree_set_limit(struct ext2fs_htree_entry *ep, uint16_t limit)
136 1.2 christos {
137 1.2 christos ((struct ext2fs_htree_count *)(ep))->h_entries_max = limit;
138 1.2 christos }
139 1.2 christos
140 1.2 christos static uint32_t
141 1.2 christos ext2fs_htree_node_limit(struct inode *ip)
142 1.2 christos {
143 1.2 christos struct m_ext2fs *fs;
144 1.2 christos uint32_t space;
145 1.2 christos
146 1.2 christos fs = ip->i_e2fs;
147 1.2 christos space = fs->e2fs_bsize - EXT2_DIR_REC_LEN(0);
148 1.2 christos
149 1.2 christos return space / sizeof(struct ext2fs_htree_entry);
150 1.2 christos }
151 1.2 christos
152 1.2 christos static int
153 1.2 christos ext2fs_htree_append_block(struct vnode *vp, char *data,
154 1.2 christos struct componentname *cnp, uint32_t blksize)
155 1.2 christos {
156 1.2 christos struct iovec aiov;
157 1.2 christos struct uio auio;
158 1.2 christos struct inode *dp = VTOI(vp);
159 1.2 christos uint64_t cursize, newsize;
160 1.2 christos int error;
161 1.2 christos
162 1.2 christos cursize = roundup(dp->i_size, blksize);
163 1.2 christos newsize = cursize + blksize;
164 1.2 christos
165 1.2 christos auio.uio_offset = cursize;
166 1.2 christos auio.uio_resid = blksize;
167 1.2 christos aiov.iov_len = blksize;
168 1.2 christos aiov.iov_base = data;
169 1.2 christos auio.uio_iov = &aiov;
170 1.2 christos auio.uio_iovcnt = 1;
171 1.2 christos auio.uio_rw = UIO_WRITE;
172 1.2 christos auio.uio_vmspace = vmspace_kernel();
173 1.2 christos error = VOP_WRITE(vp, &auio, IO_SYNC, cnp->cn_cred);
174 1.2 christos if (!error)
175 1.2 christos dp->i_size = newsize;
176 1.2 christos
177 1.2 christos return error;
178 1.2 christos }
179 1.2 christos
180 1.2 christos static int
181 1.2 christos ext2fs_htree_writebuf(struct ext2fs_htree_lookup_info *info)
182 1.2 christos {
183 1.2 christos int i, error;
184 1.2 christos
185 1.2 christos for (i = 0; i < info->h_levels_num; i++) {
186 1.2 christos struct buf *bp = info->h_levels[i].h_bp;
187 1.2 christos error = bwrite(bp);
188 1.2 christos if (error)
189 1.2 christos return error;
190 1.2 christos }
191 1.2 christos
192 1.2 christos return 0;
193 1.2 christos }
194 1.2 christos
195 1.2 christos static void
196 1.2 christos ext2fs_htree_insert_entry_to_level(struct ext2fs_htree_lookup_level *level,
197 1.2 christos uint32_t hash, uint32_t blk)
198 1.2 christos {
199 1.2 christos struct ext2fs_htree_entry *target;
200 1.2 christos int entries_num;
201 1.2 christos
202 1.2 christos target = level->h_entry + 1;
203 1.2 christos entries_num = ext2fs_htree_get_count(level->h_entries);
204 1.2 christos
205 1.2 christos memmove(target + 1, target, (char *)(level->h_entries + entries_num) -
206 1.2 christos (char *)target);
207 1.2 christos ext2fs_htree_set_block(target, blk);
208 1.2 christos ext2fs_htree_set_hash(target, hash);
209 1.2 christos ext2fs_htree_set_count(level->h_entries, entries_num + 1);
210 1.2 christos }
211 1.2 christos
212 1.2 christos /*
213 1.2 christos * Insert an index entry to the index node.
214 1.2 christos */
215 1.2 christos static void
216 1.2 christos ext2fs_htree_insert_entry(struct ext2fs_htree_lookup_info *info,
217 1.2 christos uint32_t hash, uint32_t blk)
218 1.2 christos {
219 1.2 christos struct ext2fs_htree_lookup_level *level;
220 1.2 christos
221 1.2 christos level = &info->h_levels[info->h_levels_num - 1];
222 1.2 christos ext2fs_htree_insert_entry_to_level(level, hash, blk);
223 1.2 christos }
224 1.2 christos
225 1.2 christos /*
226 1.2 christos * Compare two entry sort descriptors by name hash value.
227 1.2 christos * This is used together with qsort.
228 1.2 christos */
229 1.2 christos static int
230 1.2 christos ext2fs_htree_cmp_sort_entry(const void *e1, const void *e2)
231 1.2 christos {
232 1.2 christos const struct ext2fs_htree_sort_entry *entry1, *entry2;
233 1.2 christos
234 1.2 christos entry1 = (const struct ext2fs_htree_sort_entry *)e1;
235 1.2 christos entry2 = (const struct ext2fs_htree_sort_entry *)e2;
236 1.2 christos
237 1.2 christos if (entry1->h_hash < entry2->h_hash)
238 1.2 christos return -1;
239 1.2 christos if (entry1->h_hash > entry2->h_hash)
240 1.2 christos return 1;
241 1.2 christos return 0;
242 1.2 christos }
243 1.2 christos
244 1.2 christos /*
245 1.2 christos * Append an entry to the end of the directory block.
246 1.2 christos */
247 1.2 christos static void
248 1.2 christos ext2fs_append_entry(char *block, uint32_t blksize,
249 1.2 christos struct ext2fs_direct *last_entry, struct ext2fs_direct *new_entry)
250 1.2 christos {
251 1.2 christos uint16_t entry_len;
252 1.2 christos
253 1.2 christos entry_len = EXT2_DIR_REC_LEN(last_entry->e2d_namlen);
254 1.2 christos last_entry->e2d_reclen = entry_len;
255 1.2 christos last_entry = (struct ext2fs_direct *)((char *)last_entry + entry_len);
256 1.2 christos new_entry->e2d_reclen = block + blksize - (char *)last_entry;
257 1.2 christos memcpy(last_entry, new_entry, EXT2_DIR_REC_LEN(new_entry->e2d_namlen));
258 1.2 christos }
259 1.2 christos
260 1.2 christos /*
261 1.2 christos * Move half of entries from the old directory block to the new one.
262 1.2 christos */
263 1.2 christos static int
264 1.2 christos ext2fs_htree_split_dirblock(char *block1, char *block2, uint32_t blksize,
265 1.2 christos uint32_t *hash_seed, uint8_t hash_version,
266 1.2 christos uint32_t *split_hash, struct ext2fs_direct *entry)
267 1.2 christos {
268 1.2 christos int entry_cnt = 0;
269 1.2 christos int size = 0;
270 1.2 christos int i, k;
271 1.2 christos uint32_t offset;
272 1.2 christos uint16_t entry_len = 0;
273 1.2 christos uint32_t entry_hash;
274 1.2 christos struct ext2fs_direct *ep, *last;
275 1.2 christos char *dest;
276 1.2 christos struct ext2fs_htree_sort_entry *sort_info, dummy;
277 1.2 christos
278 1.2 christos ep = (struct ext2fs_direct *)block1;
279 1.2 christos dest = block2;
280 1.2 christos sort_info = (struct ext2fs_htree_sort_entry *)
281 1.2 christos ((char *)block2 + blksize);
282 1.2 christos
283 1.2 christos /*
284 1.2 christos * Calculate name hash value for the entry which is to be added.
285 1.2 christos */
286 1.2 christos ext2fs_htree_hash(entry->e2d_name, entry->e2d_namlen, hash_seed,
287 1.2 christos hash_version, &entry_hash, NULL);
288 1.2 christos
289 1.2 christos /*
290 1.2 christos * Fill in directory entry sort descriptors.
291 1.2 christos */
292 1.2 christos while ((char *)ep < block1 + blksize) {
293 1.2 christos if (ep->e2d_ino && ep->e2d_namlen) {
294 1.2 christos entry_cnt++;
295 1.2 christos sort_info--;
296 1.2 christos sort_info->h_size = ep->e2d_reclen;
297 1.2 christos sort_info->h_offset = (char *)ep - block1;
298 1.2 christos ext2fs_htree_hash(ep->e2d_name, ep->e2d_namlen,
299 1.2 christos hash_seed, hash_version,
300 1.2 christos &sort_info->h_hash, NULL);
301 1.2 christos }
302 1.2 christos ep = (struct ext2fs_direct *)
303 1.2 christos ((char *)ep + ep->e2d_reclen);
304 1.2 christos }
305 1.2 christos
306 1.2 christos /*
307 1.2 christos * Sort directory entry descriptors by name hash value.
308 1.2 christos */
309 1.2 christos kheapsort(sort_info, entry_cnt, sizeof(struct ext2fs_htree_sort_entry),
310 1.2 christos ext2fs_htree_cmp_sort_entry,&dummy);
311 1.2 christos
312 1.2 christos /*
313 1.2 christos * Count the number of entries to move to directory block 2.
314 1.2 christos */
315 1.2 christos for (i = entry_cnt - 1; i >= 0; i--) {
316 1.2 christos if (sort_info[i].h_size + size > blksize / 2)
317 1.2 christos break;
318 1.2 christos size += sort_info[i].h_size;
319 1.2 christos }
320 1.2 christos
321 1.2 christos *split_hash = sort_info[i + 1].h_hash;
322 1.2 christos
323 1.2 christos /*
324 1.2 christos * Set collision bit.
325 1.2 christos */
326 1.2 christos if (*split_hash == sort_info[i].h_hash)
327 1.2 christos *split_hash += 1;
328 1.2 christos
329 1.2 christos /*
330 1.2 christos * Move half of directory entries from block 1 to block 2.
331 1.2 christos */
332 1.2 christos for (k = i + 1; k < entry_cnt; k++) {
333 1.2 christos ep = (struct ext2fs_direct *)((char *)block1 +
334 1.2 christos sort_info[k].h_offset);
335 1.2 christos entry_len = EXT2_DIR_REC_LEN(ep->e2d_namlen);
336 1.2 christos memcpy(dest, ep, entry_len);
337 1.2 christos ((struct ext2fs_direct *)dest)->e2d_reclen = entry_len;
338 1.2 christos /* Mark directory entry as unused. */
339 1.2 christos ep->e2d_ino = 0;
340 1.2 christos dest += entry_len;
341 1.2 christos }
342 1.2 christos dest -= entry_len;
343 1.2 christos
344 1.2 christos /* Shrink directory entries in block 1. */
345 1.2 christos last = (struct ext2fs_direct *)block1;
346 1.2 christos entry_len = 0;
347 1.2 christos for (offset = 0; offset < blksize; ) {
348 1.2 christos ep = (struct ext2fs_direct *)(block1 + offset);
349 1.2 christos offset += ep->e2d_reclen;
350 1.2 christos if (ep->e2d_ino) {
351 1.2 christos last = (struct ext2fs_direct *)
352 1.2 christos ((char *)last + entry_len);
353 1.2 christos entry_len = EXT2_DIR_REC_LEN(ep->e2d_namlen);
354 1.2 christos memcpy((void *)last, (void *)ep, entry_len);
355 1.2 christos last->e2d_reclen = entry_len;
356 1.2 christos }
357 1.2 christos }
358 1.2 christos
359 1.2 christos if (entry_hash >= *split_hash) {
360 1.2 christos /* Add entry to block 2. */
361 1.2 christos ext2fs_append_entry(block2, blksize,
362 1.2 christos (struct ext2fs_direct *)dest, entry);
363 1.2 christos
364 1.2 christos /* Adjust length field of last entry of block 1. */
365 1.2 christos last->e2d_reclen = block1 + blksize - (char *)last;
366 1.2 christos } else {
367 1.2 christos /* Add entry to block 1. */
368 1.2 christos ext2fs_append_entry(block1, blksize, last, entry);
369 1.2 christos
370 1.2 christos /* Adjust length field of last entry of block 2. */
371 1.2 christos ((struct ext2fs_direct *)dest)->e2d_reclen =
372 1.2 christos block2 + blksize - dest;
373 1.2 christos }
374 1.2 christos
375 1.2 christos return 0;
376 1.2 christos }
377 1.2 christos
378 1.2 christos /*
379 1.2 christos * Create an HTree index for a directory having entries which are no more
380 1.2 christos * accommodable in a single dir-block.
381 1.2 christos */
382 1.2 christos int
383 1.2 christos ext2fs_htree_create_index(struct vnode *vp, struct componentname *cnp,
384 1.2 christos struct ext2fs_direct *new_entry)
385 1.2 christos {
386 1.2 christos struct buf *bp = NULL;
387 1.2 christos struct inode *dp;
388 1.2 christos struct ext2fs *fs;
389 1.2 christos struct m_ext2fs *m_fs;
390 1.2 christos struct ext2fs_direct *ep, *dotdot;
391 1.2 christos struct ext2fs_htree_root *root;
392 1.2 christos struct ext2fs_htree_lookup_info info;
393 1.2 christos uint32_t blksize, dirlen, split_hash;
394 1.2 christos uint8_t hash_version;
395 1.2 christos char *buf1 = NULL;
396 1.2 christos char *buf2 = NULL;
397 1.2 christos int error = 0;
398 1.2 christos
399 1.2 christos dp = VTOI(vp);
400 1.2 christos fs = &(dp->i_e2fs->e2fs);
401 1.2 christos m_fs = dp->i_e2fs;
402 1.2 christos blksize = m_fs->e2fs_bsize;
403 1.2 christos
404 1.2 christos buf1 = malloc(blksize, M_TEMP, M_WAITOK | M_ZERO);
405 1.2 christos buf2 = malloc(blksize, M_TEMP, M_WAITOK | M_ZERO);
406 1.2 christos
407 1.2 christos if ((error = ext2fs_blkatoff(vp, 0, NULL, &bp)) != 0)
408 1.2 christos goto out;
409 1.2 christos
410 1.2 christos root = (struct ext2fs_htree_root *)bp->b_data;
411 1.2 christos dotdot = (struct ext2fs_direct *)((char *)&(root->h_dotdot));
412 1.2 christos ep = (struct ext2fs_direct *)((char *)dotdot + dotdot->e2d_reclen);
413 1.2 christos dirlen = (char *)root + blksize - (char *)ep;
414 1.2 christos memcpy(buf1, ep, dirlen);
415 1.2 christos ep = (struct ext2fs_direct *)buf1;
416 1.2 christos while ((char *)ep < buf1 + dirlen)
417 1.2 christos ep = (struct ext2fs_direct *)((char *)ep + ep->e2d_reclen);
418 1.2 christos ep->e2d_reclen = buf1 + blksize - (char *)ep;
419 1.2 christos /* XXX It should be made dp->i_flag |= IN_E3INDEX; */
420 1.2 christos dp->i_din.e2fs_din->e2di_flags |= EXT2_INDEX;
421 1.2 christos
422 1.2 christos /*
423 1.2 christos * Initialize index root.
424 1.2 christos */
425 1.2 christos dotdot->e2d_reclen = blksize - EXT2_DIR_REC_LEN(1);
426 1.2 christos memset(&root->h_info, 0, sizeof(root->h_info));
427 1.2 christos root->h_info.h_hash_version = fs->e3fs_def_hash_version;
428 1.2 christos root->h_info.h_info_len = sizeof(root->h_info);
429 1.2 christos ext2fs_htree_set_block(root->h_entries, 1);
430 1.2 christos ext2fs_htree_set_count(root->h_entries, 1);
431 1.2 christos ext2fs_htree_set_limit(root->h_entries,
432 1.2 christos ext2fs_htree_root_limit(dp, sizeof(root->h_info)));
433 1.2 christos
434 1.2 christos memset(&info, 0, sizeof(info));
435 1.2 christos info.h_levels_num = 1;
436 1.2 christos info.h_levels[0].h_entries = root->h_entries;
437 1.2 christos info.h_levels[0].h_entry = root->h_entries;
438 1.2 christos
439 1.2 christos hash_version = root->h_info.h_hash_version;
440 1.2 christos if (hash_version <= EXT2_HTREE_TEA)
441 1.2 christos hash_version += m_fs->e2fs_uhash;
442 1.2 christos ext2fs_htree_split_dirblock(buf1, buf2, blksize, fs->e3fs_hash_seed,
443 1.2 christos hash_version, &split_hash, new_entry);
444 1.2 christos ext2fs_htree_insert_entry(&info, split_hash, 2);
445 1.2 christos
446 1.2 christos /*
447 1.2 christos * Write directory block 0.
448 1.2 christos */
449 1.2 christos if ( (vp)->v_mount->mnt_iflag & IO_SYNC)
450 1.2 christos (void)bwrite(bp);
451 1.2 christos else
452 1.2 christos bdwrite(bp);
453 1.2 christos
454 1.2 christos dp->i_flag |= IN_CHANGE | IN_UPDATE;
455 1.2 christos if (error)
456 1.2 christos goto out;
457 1.2 christos
458 1.2 christos /*
459 1.2 christos * Write directory block 1.
460 1.2 christos */
461 1.2 christos error = ext2fs_htree_append_block(vp, buf1, cnp, blksize);
462 1.2 christos if (error)
463 1.2 christos goto out1;
464 1.2 christos
465 1.2 christos /*
466 1.2 christos * Write directory block 2.
467 1.2 christos */
468 1.2 christos error = ext2fs_htree_append_block(vp, buf2, cnp, blksize);
469 1.2 christos goto out1;
470 1.2 christos out:
471 1.2 christos if (bp != NULL)
472 1.2 christos brelse(bp, 0);
473 1.2 christos out1:
474 1.2 christos free(buf1, M_TEMP);
475 1.2 christos free(buf2, M_TEMP);
476 1.2 christos return error;
477 1.2 christos }
478 1.2 christos
479 1.2 christos /*
480 1.2 christos * Add an entry to the directory using htree index.
481 1.2 christos */
482 1.2 christos int
483 1.2 christos ext2fs_htree_add_entry(struct vnode *dvp, struct ext2fs_direct *entry,
484 1.2 christos struct componentname *cnp)
485 1.2 christos {
486 1.2 christos struct ext2fs_htree_entry *entries, *leaf_node;
487 1.2 christos struct ext2fs_htree_lookup_info info;
488 1.2 christos struct buf *bp = NULL;
489 1.2 christos struct ext2fs *fs;
490 1.2 christos struct m_ext2fs *m_fs;
491 1.2 christos struct inode *ip;
492 1.2 christos uint16_t ent_num;
493 1.2 christos uint32_t dirhash, split_hash;
494 1.2 christos uint32_t blksize, blknum;
495 1.2 christos uint64_t cursize, dirsize;
496 1.2 christos uint8_t hash_version;
497 1.2 christos char *newdirblock = NULL;
498 1.2 christos char *newidxblock = NULL;
499 1.2 christos struct ext2fs_htree_node *dst_node;
500 1.2 christos struct ext2fs_htree_entry *dst_entries;
501 1.2 christos struct ext2fs_htree_entry *root_entires;
502 1.2 christos struct buf *dst_bp = NULL;
503 1.2 christos int error, write_bp = 0, write_dst_bp = 0, write_info = 0;
504 1.2 christos
505 1.2 christos ip = VTOI(dvp);
506 1.2 christos m_fs = ip->i_e2fs;
507 1.2 christos fs = &(m_fs->e2fs);
508 1.2 christos blksize = m_fs->e2fs_bsize;
509 1.2 christos
510 1.2 christos if (ip->i_crap.ulr_count != 0)
511 1.2 christos return ext2fs_add_entry(dvp, entry, &(ip->i_crap) );
512 1.2 christos /* Target directory block is full, split it */
513 1.2 christos memset(&info, 0, sizeof(info));
514 1.2 christos error = ext2fs_htree_find_leaf(ip, entry->e2d_name, entry->e2d_namlen,
515 1.2 christos &dirhash, &hash_version, &info);
516 1.2 christos if (error)
517 1.2 christos return error;
518 1.2 christos entries = info.h_levels[info.h_levels_num - 1].h_entries;
519 1.2 christos ent_num = ext2fs_htree_get_count(entries);
520 1.2 christos if (ent_num == ext2fs_htree_get_limit(entries)) {
521 1.2 christos /* Split the index node. */
522 1.2 christos root_entires = info.h_levels[0].h_entries;
523 1.2 christos newidxblock = malloc(blksize, M_TEMP, M_WAITOK | M_ZERO);
524 1.2 christos dst_node = (struct ext2fs_htree_node *)newidxblock;
525 1.2 christos dst_entries = dst_node->h_entries;
526 1.2 christos memset(&dst_node->h_fake_dirent, 0,
527 1.2 christos sizeof(dst_node->h_fake_dirent));
528 1.2 christos dst_node->h_fake_dirent.e2d_reclen = blksize;
529 1.2 christos
530 1.2 christos cursize = roundup(ip->i_size, blksize);
531 1.2 christos dirsize = cursize + blksize;
532 1.2 christos blknum = dirsize / blksize - 1;
533 1.2 christos
534 1.2 christos error = ext2fs_htree_append_block(dvp, newidxblock,
535 1.2 christos cnp, blksize);
536 1.2 christos if (error)
537 1.2 christos goto finish;
538 1.2 christos error = ext2fs_blkatoff(dvp, cursize, NULL, &dst_bp);
539 1.2 christos if (error)
540 1.2 christos goto finish;
541 1.2 christos
542 1.2 christos dst_node = (struct ext2fs_htree_node *)dst_bp->b_data;
543 1.2 christos dst_entries = dst_node->h_entries;
544 1.2 christos
545 1.2 christos if (info.h_levels_num == 2) {
546 1.2 christos uint16_t src_ent_num, dst_ent_num;
547 1.2 christos
548 1.2 christos if (ext2fs_htree_get_count(root_entires) ==
549 1.2 christos ext2fs_htree_get_limit(root_entires)) {
550 1.2 christos /* Directory index is full */
551 1.2 christos error = EIO;
552 1.2 christos goto finish;
553 1.2 christos }
554 1.2 christos
555 1.2 christos src_ent_num = ent_num / 2;
556 1.2 christos dst_ent_num = ent_num - src_ent_num;
557 1.2 christos split_hash = ext2fs_htree_get_hash(entries + src_ent_num);
558 1.2 christos
559 1.2 christos /* Move half of index entries to the new index node */
560 1.2 christos memcpy(dst_entries, entries + src_ent_num,
561 1.2 christos dst_ent_num * sizeof(struct ext2fs_htree_entry));
562 1.2 christos ext2fs_htree_set_count(entries, src_ent_num);
563 1.2 christos ext2fs_htree_set_count(dst_entries, dst_ent_num);
564 1.2 christos ext2fs_htree_set_limit(dst_entries,
565 1.2 christos ext2fs_htree_node_limit(ip));
566 1.2 christos
567 1.2 christos if (info.h_levels[1].h_entry >= entries + src_ent_num) {
568 1.2 christos struct buf *tmp = info.h_levels[1].h_bp;
569 1.2 christos info.h_levels[1].h_bp = dst_bp;
570 1.2 christos dst_bp = tmp;
571 1.2 christos
572 1.2 christos info.h_levels[1].h_entry =
573 1.2 christos info.h_levels[1].h_entry -
574 1.2 christos (entries + src_ent_num) +
575 1.2 christos dst_entries;
576 1.2 christos info.h_levels[1].h_entries = dst_entries;
577 1.2 christos }
578 1.2 christos ext2fs_htree_insert_entry_to_level(&info.h_levels[0],
579 1.2 christos split_hash, blknum);
580 1.2 christos
581 1.2 christos /* Write new index node to disk */
582 1.2 christos error = bwrite(dst_bp);
583 1.2 christos ip->i_flag |= IN_CHANGE | IN_UPDATE;
584 1.2 christos if (error)
585 1.2 christos goto finish;
586 1.2 christos
587 1.2 christos write_dst_bp = 1;
588 1.2 christos } else {
589 1.2 christos /* Create second level for htree index */
590 1.2 christos
591 1.2 christos struct ext2fs_htree_root *idx_root;
592 1.2 christos
593 1.2 christos memcpy(dst_entries, entries,
594 1.2 christos ent_num * sizeof(struct ext2fs_htree_entry));
595 1.2 christos ext2fs_htree_set_limit(dst_entries,
596 1.2 christos ext2fs_htree_node_limit(ip));
597 1.2 christos
598 1.2 christos idx_root = (struct ext2fs_htree_root *)
599 1.2 christos info.h_levels[0].h_bp->b_data;
600 1.2 christos idx_root->h_info.h_ind_levels = 1;
601 1.2 christos
602 1.2 christos ext2fs_htree_set_count(entries, 1);
603 1.2 christos ext2fs_htree_set_block(entries, blknum);
604 1.2 christos
605 1.2 christos info.h_levels_num = 2;
606 1.2 christos info.h_levels[1].h_entries = dst_entries;
607 1.2 christos info.h_levels[1].h_entry = info.h_levels[0].h_entry -
608 1.2 christos info.h_levels[0].h_entries + dst_entries;
609 1.2 christos info.h_levels[1].h_bp = dst_bp;
610 1.2 christos dst_bp = NULL;
611 1.2 christos }
612 1.2 christos }
613 1.2 christos
614 1.2 christos leaf_node = info.h_levels[info.h_levels_num - 1].h_entry;
615 1.2 christos blknum = ext2fs_htree_get_block(leaf_node);
616 1.2 christos error = ext2fs_blkatoff(dvp, blknum * blksize, NULL, &bp);
617 1.2 christos if (error)
618 1.2 christos goto finish;
619 1.2 christos
620 1.2 christos /* Split target directory block */
621 1.2 christos newdirblock = malloc(blksize, M_TEMP, M_WAITOK | M_ZERO);
622 1.2 christos ext2fs_htree_split_dirblock((char *)bp->b_data, newdirblock, blksize,
623 1.2 christos fs->e3fs_hash_seed, hash_version, &split_hash, entry);
624 1.2 christos cursize = roundup(ip->i_size, blksize);
625 1.2 christos dirsize = cursize + blksize;
626 1.2 christos blknum = dirsize / blksize - 1;
627 1.2 christos
628 1.2 christos /* Add index entry for the new directory block */
629 1.2 christos ext2fs_htree_insert_entry(&info, split_hash, blknum);
630 1.2 christos
631 1.2 christos /* Write the new directory block to the end of the directory */
632 1.2 christos error = ext2fs_htree_append_block(dvp, newdirblock, cnp, blksize);
633 1.2 christos
634 1.2 christos if (error)
635 1.2 christos goto finish;
636 1.2 christos
637 1.2 christos /* Write the target directory block */
638 1.2 christos error = bwrite(bp);
639 1.2 christos ip->i_flag |= IN_CHANGE | IN_UPDATE;
640 1.2 christos
641 1.2 christos if (error)
642 1.2 christos goto finish;
643 1.2 christos write_bp = 1;
644 1.2 christos
645 1.2 christos /* Write the index block */
646 1.2 christos error = ext2fs_htree_writebuf(&info);
647 1.2 christos if (!error)
648 1.2 christos write_info = 1;
649 1.2 christos
650 1.2 christos finish:
651 1.2 christos if (dst_bp != NULL && !write_dst_bp)
652 1.2 christos brelse(dst_bp, 0);
653 1.2 christos if (bp != NULL && !write_bp)
654 1.2 christos brelse(bp, 0);
655 1.2 christos if (newdirblock != NULL)
656 1.2 christos free(newdirblock, M_TEMP);
657 1.2 christos if (newidxblock != NULL)
658 1.2 christos free(newidxblock, M_TEMP);
659 1.2 christos if (!write_info)
660 1.2 christos ext2fs_htree_release(&info);
661 1.2 christos return error;
662 1.2 christos }
663 1.2 christos
664 1.1 christos static int
665 1.1 christos ext2fs_htree_check_next(struct inode *ip, uint32_t hash, const char *name,
666 1.1 christos struct ext2fs_htree_lookup_info *info)
667 1.1 christos {
668 1.1 christos struct vnode *vp = ITOV(ip);
669 1.1 christos struct ext2fs_htree_lookup_level *level;
670 1.1 christos struct buf *bp;
671 1.1 christos uint32_t next_hash;
672 1.1 christos int idx = info->h_levels_num - 1;
673 1.1 christos int levels = 0;
674 1.1 christos
675 1.1 christos for (;;) {
676 1.1 christos level = &info->h_levels[idx];
677 1.1 christos level->h_entry++;
678 1.1 christos if (level->h_entry < level->h_entries +
679 1.1 christos ext2fs_htree_get_count(level->h_entries))
680 1.1 christos break;
681 1.1 christos if (idx == 0)
682 1.1 christos return 0;
683 1.1 christos idx--;
684 1.1 christos levels++;
685 1.1 christos }
686 1.1 christos
687 1.1 christos next_hash = ext2fs_htree_get_hash(level->h_entry);
688 1.1 christos if ((hash & 1) == 0) {
689 1.1 christos if (hash != (next_hash & ~1))
690 1.1 christos return 0;
691 1.1 christos }
692 1.1 christos
693 1.1 christos while (levels > 0) {
694 1.1 christos levels--;
695 1.1 christos if (ext2fs_blkatoff(vp, ext2fs_htree_get_block(level->h_entry) *
696 1.1 christos ip->i_e2fs->e2fs_bsize, NULL, &bp) != 0)
697 1.1 christos return 0;
698 1.1 christos level = &info->h_levels[idx + 1];
699 1.1 christos brelse(level->h_bp, 0);
700 1.1 christos level->h_bp = bp;
701 1.1 christos level->h_entry = level->h_entries =
702 1.1 christos ((struct ext2fs_htree_node *)bp->b_data)->h_entries;
703 1.1 christos }
704 1.1 christos
705 1.1 christos return 1;
706 1.1 christos }
707 1.1 christos
708 1.1 christos static int
709 1.1 christos ext2fs_htree_find_leaf(struct inode *ip, const char *name, int namelen,
710 1.1 christos uint32_t *hash, uint8_t *hash_ver,
711 1.1 christos struct ext2fs_htree_lookup_info *info)
712 1.1 christos {
713 1.1 christos struct vnode *vp;
714 1.1 christos struct ext2fs *fs;/* F, G, and H are MD4 functions */
715 1.1 christos struct m_ext2fs *m_fs;
716 1.1 christos struct buf *bp = NULL;
717 1.1 christos struct ext2fs_htree_root *rootp;
718 1.1 christos struct ext2fs_htree_entry *entp, *start, *end, *middle, *found;
719 1.1 christos struct ext2fs_htree_lookup_level *level_info;
720 1.1 christos uint32_t hash_major = 0, hash_minor = 0;
721 1.1 christos uint32_t levels, cnt;
722 1.1 christos uint8_t hash_version;
723 1.1 christos
724 1.1 christos if (name == NULL || info == NULL)
725 1.1 christos return -1;
726 1.1 christos
727 1.1 christos vp = ITOV(ip);
728 1.1 christos fs = &(ip->i_e2fs->e2fs);
729 1.1 christos m_fs = ip->i_e2fs;
730 1.1 christos
731 1.1 christos if (ext2fs_blkatoff(vp, 0, NULL, &bp) != 0)
732 1.1 christos return -1;
733 1.1 christos
734 1.1 christos info->h_levels_num = 1;
735 1.1 christos info->h_levels[0].h_bp = bp;
736 1.1 christos rootp = (struct ext2fs_htree_root *)bp->b_data;
737 1.1 christos if (rootp->h_info.h_hash_version != EXT2_HTREE_LEGACY &&
738 1.1 christos rootp->h_info.h_hash_version != EXT2_HTREE_HALF_MD4 &&
739 1.1 christos rootp->h_info.h_hash_version != EXT2_HTREE_TEA)
740 1.1 christos goto error;
741 1.1 christos
742 1.1 christos hash_version = rootp->h_info.h_hash_version;
743 1.1 christos if (hash_version <= EXT2_HTREE_TEA)
744 1.1 christos hash_version += m_fs->e2fs_uhash;
745 1.1 christos *hash_ver = hash_version;
746 1.1 christos
747 1.1 christos ext2fs_htree_hash(name, namelen, fs->e3fs_hash_seed,
748 1.1 christos hash_version, &hash_major, &hash_minor);
749 1.1 christos *hash = hash_major;
750 1.1 christos
751 1.1 christos if ((levels = rootp->h_info.h_ind_levels) > 1)
752 1.1 christos goto error;
753 1.1 christos
754 1.1 christos entp = (struct ext2fs_htree_entry *)(((char *)&rootp->h_info) +
755 1.1 christos rootp->h_info.h_info_len);
756 1.1 christos
757 1.1 christos if (ext2fs_htree_get_limit(entp) !=
758 1.1 christos ext2fs_htree_root_limit(ip, rootp->h_info.h_info_len))
759 1.1 christos goto error;
760 1.1 christos
761 1.1 christos for (;;) {
762 1.1 christos cnt = ext2fs_htree_get_count(entp);
763 1.1 christos if (cnt == 0 || cnt > ext2fs_htree_get_limit(entp))
764 1.1 christos goto error;
765 1.1 christos
766 1.1 christos start = entp + 1;
767 1.1 christos end = entp + cnt - 1;
768 1.1 christos while (start <= end) {
769 1.1 christos middle = start + (end - start) / 2;
770 1.1 christos if (ext2fs_htree_get_hash(middle) > hash_major)
771 1.1 christos end = middle - 1;
772 1.1 christos else
773 1.1 christos start = middle + 1;
774 1.1 christos }
775 1.1 christos found = start - 1;
776 1.1 christos
777 1.1 christos level_info = &(info->h_levels[info->h_levels_num - 1]);
778 1.1 christos level_info->h_bp = bp;
779 1.1 christos level_info->h_entries = entp;
780 1.1 christos level_info->h_entry = found;
781 1.1 christos if (levels == 0)
782 1.1 christos return (0);
783 1.1 christos levels--;
784 1.1 christos if (ext2fs_blkatoff(vp,
785 1.1 christos ext2fs_htree_get_block(found) * m_fs->e2fs_bsize,
786 1.1 christos NULL, &bp) != 0)
787 1.1 christos goto error;
788 1.1 christos entp = ((struct ext2fs_htree_node *)bp->b_data)->h_entries;
789 1.1 christos info->h_levels_num++;
790 1.1 christos info->h_levels[info->h_levels_num - 1].h_bp = bp;
791 1.1 christos }
792 1.1 christos
793 1.1 christos error:
794 1.1 christos ext2fs_htree_release(info);
795 1.1 christos return -1;
796 1.1 christos }
797 1.1 christos
798 1.1 christos /*
799 1.1 christos * Try to lookup a directory entry in HTree index
800 1.1 christos */
801 1.1 christos int
802 1.1 christos ext2fs_htree_lookup(struct inode *ip, const char *name, int namelen,
803 1.1 christos struct buf **bpp, int *entryoffp, doff_t *offp,
804 1.1 christos doff_t *prevoffp, doff_t *endusefulp, struct ext2fs_searchslot *ss)
805 1.1 christos {
806 1.1 christos struct vnode *vp;
807 1.1 christos struct ext2fs_htree_lookup_info info;
808 1.1 christos struct ext2fs_htree_entry *leaf_node;
809 1.1 christos struct m_ext2fs *m_fs;
810 1.1 christos struct buf *bp;
811 1.1 christos uint32_t blk;
812 1.1 christos uint32_t dirhash;
813 1.1 christos uint32_t bsize;
814 1.1 christos uint8_t hash_version;
815 1.1 christos int search_next;
816 1.1 christos
817 1.1 christos m_fs = ip->i_e2fs;
818 1.1 christos bsize = m_fs->e2fs_bsize;
819 1.1 christos vp = ITOV(ip);
820 1.1 christos
821 1.1 christos /* TODO: print error msg because we don't lookup '.' and '..' */
822 1.1 christos
823 1.1 christos memset(&info, 0, sizeof(info));
824 1.1 christos if (ext2fs_htree_find_leaf(ip, name, namelen, &dirhash,
825 1.1 christos &hash_version, &info)) {
826 1.1 christos return -1;
827 1.1 christos }
828 1.1 christos
829 1.1 christos do {
830 1.1 christos leaf_node = info.h_levels[info.h_levels_num - 1].h_entry;
831 1.1 christos blk = ext2fs_htree_get_block(leaf_node);
832 1.1 christos if (ext2fs_blkatoff(vp, blk * bsize, NULL, &bp) != 0) {
833 1.1 christos ext2fs_htree_release(&info);
834 1.1 christos return -1;
835 1.1 christos }
836 1.1 christos
837 1.1 christos *offp = blk * bsize;
838 1.1 christos *entryoffp = 0;
839 1.1 christos *prevoffp = blk * bsize;
840 1.1 christos *endusefulp = blk * bsize;
841 1.1 christos
842 1.1 christos if (ss->slotstatus == NONE) {
843 1.1 christos ss->slotoffset = -1;
844 1.1 christos ss->slotfreespace = 0;
845 1.1 christos }
846 1.1 christos
847 1.1 christos int found;
848 1.1 christos if (ext2fs_search_dirblock(ip, bp->b_data, &found,
849 1.1 christos name, namelen, entryoffp, offp, prevoffp,
850 1.1 christos endusefulp, ss) != 0) {
851 1.1 christos brelse(bp, 0);
852 1.1 christos ext2fs_htree_release(&info);
853 1.1 christos return -1;
854 1.1 christos }
855 1.1 christos
856 1.1 christos if (found) {
857 1.1 christos *bpp = bp;
858 1.1 christos ext2fs_htree_release(&info);
859 1.1 christos return 0;
860 1.1 christos }
861 1.1 christos
862 1.1 christos brelse(bp,0);
863 1.1 christos search_next = ext2fs_htree_check_next(ip, dirhash, name, &info);
864 1.1 christos } while (search_next);
865 1.1 christos
866 1.1 christos ext2fs_htree_release(&info);
867 1.1 christos return ENOENT;
868 1.1 christos }
869