xfs: Fix memory leak in xfs_dir2_node_find_entry() function
[syslinux.git] / core / fs / xfs / xfs_dir2.c
blob30db8cca13e6ec834d4e24cc81ded5ce22e089a6
1 /*
2 * Copyright (c) 2012 Paulo Alcantara <pcacjr@zytor.com>
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation.
8 * This program is distributed in the hope that it would be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, write the Free Software Foundation,
15 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 #include <cache.h>
19 #include <core.h>
20 #include <fs.h>
22 #include "xfs_types.h"
23 #include "xfs_sb.h"
24 #include "xfs_ag.h"
25 #include "misc.h"
26 #include "xfs.h"
27 #include "xfs_dinode.h"
29 #include "xfs_dir2.h"
31 char *xfs_dir2_get_entry_name(uint8_t *start, uint8_t *end)
33 char *s;
34 char *p;
36 s = malloc(end - start + 1);
37 if (!s)
38 malloc_error("string");
40 p = s;
41 while (start < end)
42 *p++ = *start++;
44 *p = '\0';
46 return s;
49 uint32_t xfs_dir2_da_hashname(const uint8_t *name, int namelen)
51 uint32_t hash;
54 * Do four characters at a time as long as we can.
56 for (hash = 0; namelen >= 4; namelen -=4, name += 4)
57 hash = (name[0] << 21) ^ (name[1] << 14) ^ (name[2] << 7) ^
58 (name[3] << 0) ^ rol32(hash, 7 * 4);
61 * Now do the rest of the characters.
63 switch (namelen) {
64 case 3:
65 return (name[0] << 14) ^ (name[1] << 7) ^ (name[2] << 0) ^
66 rol32(hash, 7 * 3);
67 case 2:
68 return (name[0] << 7) ^ (name[1] << 0) ^ rol32(hash, 7 * 2);
69 case 1:
70 return (name[0] << 0) ^ rol32(hash, 7 * 1);
71 default: /* case 0: */
72 return hash;
76 void *xfs_dir2_get_dirblks(struct fs_info *fs, block_t startblock,
77 xfs_filblks_t c)
79 int count = c << XFS_INFO(fs)->dirblklog;
80 uint8_t *p;
81 uint8_t *buf;
82 off_t offset = 0;
84 buf = malloc(c * XFS_INFO(fs)->dirblksize);
85 if (!buf)
86 malloc_error("buffer memory");
88 memset(buf, 0, XFS_INFO(fs)->dirblksize);
90 while (count--) {
91 p = (uint8_t *)get_cache(fs->fs_dev, startblock++);
92 memcpy(buf + offset, p, BLOCK_SIZE(fs));
93 offset += BLOCK_SIZE(fs);
96 return buf;
99 struct inode *xfs_dir2_local_find_entry(const char *dname, struct inode *parent,
100 xfs_dinode_t *core)
102 xfs_dir2_sf_t *sf = (xfs_dir2_sf_t *)&core->di_literal_area[0];
103 xfs_dir2_sf_entry_t *sf_entry;
104 uint8_t count = sf->hdr.i8count ? sf->hdr.i8count : sf->hdr.count;
105 struct fs_info *fs = parent->fs;
106 struct inode *inode;
107 xfs_intino_t ino;
108 xfs_dinode_t *ncore = NULL;
110 xfs_debug("count %hhu i8count %hhu", sf->hdr.count, sf->hdr.i8count);
112 sf_entry = (xfs_dir2_sf_entry_t *)((uint8_t *)&sf->list[0] -
113 (!sf->hdr.i8count ? 4 : 0));
114 while (count--) {
115 uint8_t *start_name = &sf_entry->name[0];
116 uint8_t *end_name = start_name + sf_entry->namelen;
117 char *name;
119 name = xfs_dir2_get_entry_name(start_name, end_name);
121 xfs_debug("entry name: %s", name);
123 if (!strncmp(name, dname, strlen(dname))) {
124 free(name);
125 goto found;
128 free(name);
130 sf_entry = (xfs_dir2_sf_entry_t *)((uint8_t *)sf_entry +
131 offsetof(struct xfs_dir2_sf_entry,
132 name[0]) +
133 sf_entry->namelen +
134 (sf->hdr.i8count ? 8 : 4));
137 return NULL;
139 found:
140 inode = xfs_new_inode(fs);
142 ino = xfs_dir2_sf_get_inumber(sf, (xfs_dir2_inou_t *)(
143 (uint8_t *)sf_entry +
144 offsetof(struct xfs_dir2_sf_entry,
145 name[0]) +
146 sf_entry->namelen));
148 xfs_debug("entry inode's number %lu", ino);
150 ncore = xfs_dinode_get_core(fs, ino);
151 if (!ncore) {
152 xfs_error("Failed to get dinode!");
153 goto out;
156 fill_xfs_inode_pvt(fs, inode, ino);
158 inode->ino = ino;
159 inode->size = be64_to_cpu(ncore->di_size);
161 if (be16_to_cpu(ncore->di_mode) & S_IFDIR) {
162 inode->mode = DT_DIR;
163 xfs_debug("Found a directory inode!");
164 } else if (be16_to_cpu(ncore->di_mode) & S_IFREG) {
165 inode->mode = DT_REG;
166 xfs_debug("Found a file inode!");
167 xfs_debug("inode size %llu", inode->size);
170 return inode;
172 out:
173 free(inode);
175 return NULL;
178 struct inode *xfs_dir2_block_find_entry(const char *dname, struct inode *parent,
179 xfs_dinode_t *core)
181 xfs_bmbt_irec_t r;
182 block_t dir_blk;
183 struct fs_info *fs = parent->fs;
184 uint8_t *dirblk_buf;
185 uint8_t *p, *endp;
186 xfs_dir2_data_hdr_t *hdr;
187 struct inode *inode = NULL;
188 xfs_dir2_block_tail_t *btp;
189 xfs_dir2_data_unused_t *dup;
190 xfs_dir2_data_entry_t *dep;
191 xfs_intino_t ino;
192 xfs_dinode_t *ncore;
194 bmbt_irec_get(&r, (xfs_bmbt_rec_t *)&core->di_literal_area[0]);
195 dir_blk = fsblock_to_bytes(fs, r.br_startblock) >> BLOCK_SHIFT(fs);
197 dirblk_buf = xfs_dir2_get_dirblks(fs, dir_blk, r.br_blockcount);
198 hdr = (xfs_dir2_data_hdr_t *)dirblk_buf;
199 if (be32_to_cpu(hdr->magic) != XFS_DIR2_BLOCK_MAGIC) {
200 xfs_error("Block directory header's magic number does not match!");
201 xfs_debug("hdr->magic: 0x%lx", be32_to_cpu(hdr->magic));
202 goto out;
205 p = (uint8_t *)(hdr + 1);
207 btp = xfs_dir2_block_tail_p(XFS_INFO(fs), hdr);
208 endp = (uint8_t *)((xfs_dir2_leaf_entry_t *)btp - be32_to_cpu(btp->count));
210 while (p < endp) {
211 uint8_t *start_name;
212 uint8_t *end_name;
213 char *name;
215 dup = (xfs_dir2_data_unused_t *)p;
216 if (be16_to_cpu(dup->freetag) == XFS_DIR2_DATA_FREE_TAG) {
217 p += be16_to_cpu(dup->length);
218 continue;
221 dep = (xfs_dir2_data_entry_t *)p;
223 start_name = &dep->name[0];
224 end_name = start_name + dep->namelen;
225 name = xfs_dir2_get_entry_name(start_name, end_name);
227 if (!strncmp(name, dname, strlen(dname))) {
228 free(name);
229 goto found;
232 free(name);
233 p += xfs_dir2_data_entsize(dep->namelen);
236 out:
237 free(dirblk_buf);
239 return NULL;
241 found:
242 inode = xfs_new_inode(fs);
244 ino = be64_to_cpu(dep->inumber);
246 xfs_debug("entry inode's number %lu", ino);
248 ncore = xfs_dinode_get_core(fs, ino);
249 if (!ncore) {
250 xfs_error("Failed to get dinode!");
251 goto failed;
254 fill_xfs_inode_pvt(fs, inode, ino);
256 inode->ino = ino;
257 XFS_PVT(inode)->i_ino_blk = ino_to_bytes(fs, ino) >> BLOCK_SHIFT(fs);
258 inode->size = be64_to_cpu(ncore->di_size);
260 if (be16_to_cpu(ncore->di_mode) & S_IFDIR) {
261 inode->mode = DT_DIR;
262 xfs_debug("Found a directory inode!");
263 } else if (be16_to_cpu(ncore->di_mode) & S_IFREG) {
264 inode->mode = DT_REG;
265 xfs_debug("Found a file inode!");
266 xfs_debug("inode size %llu", inode->size);
269 xfs_debug("entry inode's number %lu", ino);
271 free(dirblk_buf);
272 return inode;
274 failed:
275 free(inode);
276 free(dirblk_buf);
278 return NULL;
281 struct inode *xfs_dir2_leaf_find_entry(const char *dname, struct inode *parent,
282 xfs_dinode_t *core)
284 xfs_dir2_leaf_t *leaf;
285 xfs_bmbt_irec_t irec;
286 block_t leaf_blk, dir_blk;
287 xfs_dir2_leaf_entry_t *lep;
288 int low;
289 int high;
290 int mid = 0;
291 uint32_t hash = 0;
292 uint32_t hashwant;
293 uint32_t newdb, curdb = -1;
294 xfs_dir2_data_entry_t *dep;
295 struct inode *ip;
296 xfs_dir2_data_hdr_t *data_hdr;
297 uint8_t *start_name;
298 uint8_t *end_name;
299 char *name;
300 xfs_intino_t ino;
301 xfs_dinode_t *ncore;
302 uint8_t *buf = NULL;
304 bmbt_irec_get(&irec, ((xfs_bmbt_rec_t *)&core->di_literal_area[0]) +
305 be32_to_cpu(core->di_nextents) - 1);
306 leaf_blk = fsblock_to_bytes(parent->fs, irec.br_startblock) >>
307 BLOCK_SHIFT(parent->fs);
309 leaf = (xfs_dir2_leaf_t *)xfs_dir2_get_dirblks(parent->fs, leaf_blk,
310 irec.br_blockcount);
311 if (be16_to_cpu(leaf->hdr.info.magic) != XFS_DIR2_LEAF1_MAGIC) {
312 xfs_error("Single leaf block header's magic number does not match!");
313 goto out;
316 if (!leaf->hdr.count)
317 goto out;
319 hashwant = xfs_dir2_da_hashname((uint8_t *)dname, strlen(dname));
321 /* Binary search */
322 for (lep = leaf->ents, low = 0, high = be16_to_cpu(leaf->hdr.count) - 1;
323 low <= high; ) {
324 mid = (low + high) >> 1;
325 if ((hash = be32_to_cpu(lep[mid].hashval)) == hashwant)
326 break;
327 if (hash < hashwant)
328 low = mid + 1;
329 else
330 high = mid - 1;
333 /* If hash is not the one we want, then the directory does not contain the
334 * entry we're looking for and there is nothing to do anymore.
336 if (hash != hashwant)
337 goto out;
339 while (mid > 0 && be32_to_cpu(lep[mid - 1].hashval) == hashwant)
340 mid--;
342 for (lep = &leaf->ents[mid];
343 mid < be16_to_cpu(leaf->hdr.count) &&
344 be32_to_cpu(lep->hashval) == hashwant;
345 lep++, mid++) {
346 /* Skip over stale leaf entries. */
347 if (be32_to_cpu(lep->address) == XFS_DIR2_NULL_DATAPTR)
348 continue;
350 newdb = xfs_dir2_dataptr_to_db(parent->fs, be32_to_cpu(lep->address));
351 if (newdb != curdb) {
352 if (buf)
353 free(buf);
355 bmbt_irec_get(&irec,
356 ((xfs_bmbt_rec_t *)&core->di_literal_area[0]) + newdb);
357 dir_blk = fsblock_to_bytes(parent->fs, irec.br_startblock) >>
358 BLOCK_SHIFT(parent->fs);
359 buf = xfs_dir2_get_dirblks(parent->fs, dir_blk, irec.br_blockcount);
360 data_hdr = (xfs_dir2_data_hdr_t *)buf;
361 if (be32_to_cpu(data_hdr->magic) != XFS_DIR2_DATA_MAGIC) {
362 xfs_error("Leaf directory's data magic No. does not match!");
363 goto out1;
366 curdb = newdb;
369 dep = (xfs_dir2_data_entry_t *)((char *)buf +
370 xfs_dir2_dataptr_to_off(parent->fs, be32_to_cpu(lep->address)));
372 start_name = &dep->name[0];
373 end_name = start_name + dep->namelen;
374 name = xfs_dir2_get_entry_name(start_name, end_name);
376 if (!strncmp(name, dname, strlen(dname))) {
377 free(name);
378 goto found;
381 free(name);
384 out1:
385 free(buf);
386 out:
387 free(leaf);
389 return NULL;
391 found:
392 ip = xfs_new_inode(parent->fs);
394 ino = be64_to_cpu(dep->inumber);
396 xfs_debug("entry inode's number %lu", ino);
398 ncore = xfs_dinode_get_core(parent->fs, ino);
399 if (!ncore) {
400 xfs_error("Failed to get dinode!");
401 goto failed;
404 fill_xfs_inode_pvt(parent->fs, ip, ino);
406 ip->ino = ino;
407 XFS_PVT(ip)->i_ino_blk = ino_to_bytes(parent->fs, ino) >>
408 BLOCK_SHIFT(parent->fs);
409 ip->size = be64_to_cpu(ncore->di_size);
411 if (be16_to_cpu(ncore->di_mode) & S_IFDIR) {
412 ip->mode = DT_DIR;
413 xfs_debug("Found a directory inode!");
414 } else if (be16_to_cpu(ncore->di_mode) & S_IFREG) {
415 ip->mode = DT_REG;
416 xfs_debug("Found a file inode!");
417 xfs_debug("inode size %llu", ip->size);
420 xfs_debug("entry inode's number %lu", ino);
422 free(buf);
423 free(leaf);
425 return ip;
427 failed:
428 free(ip);
429 free(buf);
430 free(leaf);
432 return ip;
435 block_t xfs_dir2_get_right_blk(struct fs_info *fs, xfs_dinode_t *core,
436 uint32_t from, uint32_t to, block_t fsblkno,
437 int *error)
439 uint32_t idx;
440 xfs_bmbt_irec_t irec;
442 *error = 0;
443 for (idx = from; idx < to; idx++) {
444 bmbt_irec_get(&irec,
445 ((xfs_bmbt_rec_t *)&core->di_literal_area[0]) + idx);
446 if (fsblkno >= irec.br_startoff &&
447 fsblkno < irec.br_startoff + irec.br_blockcount)
448 break;
451 if (fsblkno < irec.br_startoff ||
452 fsblkno >= irec.br_startoff + irec.br_blockcount)
453 *error = 1;
455 return fsblock_to_bytes(fs,
456 fsblkno - irec.br_startoff + irec.br_startblock) >>
457 BLOCK_SHIFT(fs);
460 struct inode *xfs_dir2_node_find_entry(const char *dname, struct inode *parent,
461 xfs_dinode_t *core)
463 xfs_bmbt_irec_t irec;
464 uint32_t node_off = 0;
465 block_t fsblkno;
466 xfs_da_intnode_t *node = NULL;
467 uint32_t hashwant;
468 uint32_t hash = 0;
469 xfs_da_node_entry_t *btree;
470 uint16_t max;
471 uint16_t span;
472 uint16_t probe;
473 int error;
474 xfs_dir2_data_hdr_t *data_hdr;
475 xfs_dir2_leaf_t *leaf;
476 xfs_dir2_leaf_entry_t *lep;
477 xfs_dir2_data_entry_t *dep;
478 struct inode *ip;
479 uint8_t *start_name;
480 uint8_t *end_name;
481 char *name;
482 int low;
483 int high;
484 int mid = 0;
485 uint32_t newdb, curdb = -1;
486 xfs_intino_t ino;
487 xfs_dinode_t *ncore;
488 uint8_t *buf = NULL;
490 do {
491 bmbt_irec_get(&irec, ((xfs_bmbt_rec_t *)&core->di_literal_area[0]) +
492 (++node_off));
493 } while (irec.br_startoff <
494 xfs_dir2_byte_to_db(parent->fs, XFS_DIR2_LEAF_OFFSET));
496 fsblkno = fsblock_to_bytes(parent->fs, irec.br_startblock) >>
497 BLOCK_SHIFT(parent->fs);
499 node = (xfs_da_intnode_t *)xfs_dir2_get_dirblks(parent->fs, fsblkno, 1);
500 if (be16_to_cpu(node->hdr.info.magic) != XFS_DA_NODE_MAGIC) {
501 xfs_error("Node's magic number does not match!");
502 goto out;
505 if (!node->hdr.count)
506 goto out;
508 hashwant = xfs_dir2_da_hashname((uint8_t *)dname, strlen(dname));
510 /* Given a hash to lookup, you read the node's btree array and first
511 * "hashval" in the array that exceeds the given hash and it can then
512 * be found in the block pointed by the "before" value.
514 max = be16_to_cpu(node->hdr.count);
516 probe = span = max/2;
517 for (btree = &node->btree[probe]; span > 4; btree = &node->btree[probe]) {
518 span /= 2;
519 hash = be32_to_cpu(btree->hashval);
520 if (hash < hashwant)
521 probe += span;
522 else if (hash > hashwant)
523 probe -= span;
524 else
525 break;
528 while ((probe > 0) && (be32_to_cpu(btree->hashval) >= hashwant)) {
529 btree--;
530 probe--;
532 while ((probe < max) && (be32_to_cpu(btree->hashval) < hashwant)) {
533 btree++;
534 probe++;
537 if (probe == max)
538 fsblkno = be32_to_cpu(node->btree[max-1].before);
539 else
540 fsblkno = be32_to_cpu(node->btree[probe].before);
542 fsblkno = xfs_dir2_get_right_blk(parent->fs, core, node_off + 1,
543 be32_to_cpu(core->di_nextents) - 1,
544 fsblkno, &error);
545 if (error) {
546 xfs_error("Cannot find leaf rec!");
547 goto out;
550 leaf = (xfs_dir2_leaf_t*)xfs_dir2_get_dirblks(parent->fs, fsblkno, 1);
551 if (be16_to_cpu(leaf->hdr.info.magic) != XFS_DIR2_LEAFN_MAGIC) {
552 xfs_error("Leaf's magic number does not match!");
553 goto out1;
556 if (!leaf->hdr.count)
557 goto out1;
559 for (lep = leaf->ents, low = 0, high = be16_to_cpu(leaf->hdr.count) - 1;
560 low <= high; ) {
561 mid = (low + high) >> 1;
562 if ((hash = be32_to_cpu(lep[mid].hashval)) == hashwant)
563 break;
564 if (hash < hashwant)
565 low = mid + 1;
566 else
567 high = mid - 1;
570 /* If hash is not the one we want, then the directory does not contain the
571 * entry we're looking for and there is nothing to do anymore.
573 if (hash != hashwant)
574 goto out1;
576 while (mid > 0 && be32_to_cpu(lep[mid - 1].hashval) == hashwant)
577 mid--;
579 for (lep = &leaf->ents[mid];
580 mid < be16_to_cpu(leaf->hdr.count) &&
581 be32_to_cpu(lep->hashval) == hashwant;
582 lep++, mid++) {
583 /* Skip over stale leaf entries. */
584 if (be32_to_cpu(lep->address) == XFS_DIR2_NULL_DATAPTR)
585 continue;
587 newdb = xfs_dir2_dataptr_to_db(parent->fs, be32_to_cpu(lep->address));
588 if (newdb != curdb) {
589 if (buf)
590 free(buf);
592 fsblkno = xfs_dir2_get_right_blk(parent->fs, core, 0, node_off,
593 newdb, &error);
594 if (error) {
595 xfs_error("Cannot find data block!");
596 goto out1;
599 buf = xfs_dir2_get_dirblks(parent->fs, fsblkno, 1);
600 data_hdr = (xfs_dir2_data_hdr_t *)buf;
601 if (be32_to_cpu(data_hdr->magic) != XFS_DIR2_DATA_MAGIC) {
602 xfs_error("Leaf directory's data magic No. does not match!");
603 goto out2;
605 curdb = newdb;
607 dep = (xfs_dir2_data_entry_t *)((char *)buf +
608 xfs_dir2_dataptr_to_off(parent->fs, be32_to_cpu(lep->address)));
610 start_name = &dep->name[0];
611 end_name = start_name + dep->namelen;
612 name = xfs_dir2_get_entry_name(start_name, end_name);
613 if (!strncmp(name, dname, strlen(dname))) {
614 free(name);
615 goto found;
617 free(name);
620 out2:
621 free(buf);
623 out1:
624 free(leaf);
626 out:
627 free(node);
629 return NULL;
631 found:
632 ip = xfs_new_inode(parent->fs);
633 ino = be64_to_cpu(dep->inumber);
634 ncore = xfs_dinode_get_core(parent->fs, ino);
635 if (!ncore) {
636 xfs_error("Failed to get dinode!");
637 goto failed;
640 fill_xfs_inode_pvt(parent->fs, ip, ino);
641 ip->ino = ino;
642 XFS_PVT(ip)->i_ino_blk = ino_to_bytes(parent->fs, ino) >>
643 BLOCK_SHIFT(parent->fs);
644 ip->size = be64_to_cpu(ncore->di_size);
646 if (be16_to_cpu(ncore->di_mode) & S_IFDIR) {
647 ip->mode = DT_DIR;
648 xfs_debug("Found a directory inode!");
649 } else if (be16_to_cpu(ncore->di_mode) & S_IFREG) {
650 ip->mode = DT_REG;
651 xfs_debug("Found a file inode!");
652 xfs_debug("inode size %llu", ip->size);
655 xfs_debug("entry inode's number %lu", ino);
657 free(buf);
658 free(leaf);
659 free(node);
661 return ip;
663 failed:
664 free(ip);
665 free(buf);
666 free(leaf);
667 free(node);
669 return NULL;