Merge tag 'u-boot-atmel-fixes-2021.01-b' of https://gitlab.denx.de/u-boot/custodians...
[platform/kernel/u-boot.git] / fs / squashfs / sqfs.c
index 58b8bfc..5de69ac 100644 (file)
@@ -49,6 +49,7 @@ static int sqfs_read_sblk(struct squashfs_super_block **sblk)
 
        if (sqfs_disk_read(0, 1, *sblk) != 1) {
                free(*sblk);
+               sblk = NULL;
                return -EINVAL;
        }
 
@@ -106,6 +107,10 @@ static int sqfs_frag_lookup(u32 inode_fragment_index,
        int block, offset, ret;
        u16 header;
 
+       metadata_buffer = NULL;
+       entries = NULL;
+       table = NULL;
+
        if (inode_fragment_index >= get_unaligned_le32(&sblk->fragments))
                return -EINVAL;
 
@@ -117,12 +122,14 @@ static int sqfs_frag_lookup(u32 inode_fragment_index,
 
        /* Allocate a proper sized buffer to store the fragment index table */
        table = malloc_cache_aligned(n_blks * ctxt.cur_dev->blksz);
-       if (!table)
-               return -ENOMEM;
+       if (!table) {
+               ret = -ENOMEM;
+               goto out;
+       }
 
        if (sqfs_disk_read(start, n_blks, table) < 0) {
-               free(table);
-               return -EINVAL;
+               ret = -EINVAL;
+               goto out;
        }
 
        block = SQFS_FRAGMENT_INDEX(inode_fragment_index);
@@ -142,12 +149,12 @@ static int sqfs_frag_lookup(u32 inode_fragment_index,
        metadata_buffer = malloc_cache_aligned(n_blks * ctxt.cur_dev->blksz);
        if (!metadata_buffer) {
                ret = -ENOMEM;
-               goto free_table;
+               goto out;
        }
 
        if (sqfs_disk_read(start, n_blks, metadata_buffer) < 0) {
                ret = -EINVAL;
-               goto free_buffer;
+               goto out;
        }
 
        /* Every metadata block starts with a 16-bit header */
@@ -156,13 +163,13 @@ static int sqfs_frag_lookup(u32 inode_fragment_index,
 
        if (!metadata || !header) {
                ret = -ENOMEM;
-               goto free_buffer;
+               goto out;
        }
 
        entries = malloc(SQFS_METADATA_BLOCK_SIZE);
        if (!entries) {
                ret = -ENOMEM;
-               goto free_buffer;
+               goto out;
        }
 
        if (SQFS_COMPRESSED_METADATA(header)) {
@@ -172,7 +179,7 @@ static int sqfs_frag_lookup(u32 inode_fragment_index,
                                      src_len);
                if (ret) {
                        ret = -EINVAL;
-                       goto free_entries;
+                       goto out;
                }
        } else {
                memcpy(entries, metadata, SQFS_METADATA_SIZE(header));
@@ -181,11 +188,9 @@ static int sqfs_frag_lookup(u32 inode_fragment_index,
        *e = entries[offset];
        ret = SQFS_COMPRESSED_BLOCK(e->size);
 
-free_entries:
+out:
        free(entries);
-free_buffer:
        free(metadata_buffer);
-free_table:
        free(table);
 
        return ret;
@@ -242,6 +247,9 @@ static char *sqfs_concat_tokens(char **token_list, int token_count)
        length = sqfs_get_tokens_length(token_list, token_count);
 
        result = malloc(length + 1);
+       if (!result)
+               return NULL;
+
        result[length] = '\0';
 
        for (i = 0; i < token_count; i++) {
@@ -333,28 +341,31 @@ static char *sqfs_get_abs_path(const char *base, const char *rel)
        char **base_tokens, **rel_tokens, *resolved = NULL;
        int ret, bc, rc, i, updir = 0, resolved_size = 0, offset = 0;
 
+       base_tokens = NULL;
+       rel_tokens = NULL;
+
        /* Memory allocation for the token lists */
        bc = sqfs_count_tokens(base);
        rc = sqfs_count_tokens(rel);
        if (bc < 1 || rc < 1)
                return NULL;
 
-       base_tokens = malloc(bc * sizeof(char *));
+       base_tokens = calloc(bc, sizeof(char *));
        if (!base_tokens)
                return NULL;
 
-       rel_tokens = malloc(rc * sizeof(char *));
+       rel_tokens = calloc(rc, sizeof(char *));
        if (!rel_tokens)
-               goto free_b_tokens;
+               goto out;
 
        /* Fill token lists */
        ret = sqfs_tokenize(base_tokens, bc, base);
        if (ret)
-               goto free_r_tokens;
+               goto out;
 
-       sqfs_tokenize(rel_tokens, rc, rel);
+       ret = sqfs_tokenize(rel_tokens, rc, rel);
        if (ret)
-               goto free_r_tokens;
+               goto out;
 
        /* count '..' occurrences in target path */
        for (i = 0; i < rc; i++) {
@@ -365,7 +376,7 @@ static char *sqfs_get_abs_path(const char *base, const char *rel)
        /* Remove the last token and the '..' occurrences */
        bc = sqfs_clean_base_path(base_tokens, bc, updir);
        if (bc < 0)
-               goto free_r_tokens;
+               goto out;
 
        /* Calculate resolved path size */
        if (!bc)
@@ -376,7 +387,7 @@ static char *sqfs_get_abs_path(const char *base, const char *rel)
 
        resolved = malloc(resolved_size + 1);
        if (!resolved)
-               goto free_r_tokens_loop;
+               goto out;
 
        /* Set resolved path */
        memset(resolved, '\0', resolved_size + 1);
@@ -384,14 +395,15 @@ static char *sqfs_get_abs_path(const char *base, const char *rel)
        resolved[offset++] = '/';
        offset += sqfs_join(rel_tokens, resolved + offset, updir, rc, '/');
 
-free_r_tokens_loop:
-       for (i = 0; i < rc; i++)
-               free(rel_tokens[i]);
-       for (i = 0; i < bc; i++)
-               free(base_tokens[i]);
-free_r_tokens:
+out:
+       if (rel_tokens)
+               for (i = 0; i < rc; i++)
+                       free(rel_tokens[i]);
+       if (base_tokens)
+               for (i = 0; i < bc; i++)
+                       free(base_tokens[i]);
+
        free(rel_tokens);
-free_b_tokens:
        free(base_tokens);
 
        return resolved;
@@ -434,7 +446,7 @@ static int sqfs_search_dir(struct squashfs_dir_stream *dirs, char **token_list,
 {
        struct squashfs_super_block *sblk = ctxt.sblk;
        char *path, *target, **sym_tokens, *res, *rem;
-       int j, ret, new_inode_number, offset;
+       int j, ret = 0, new_inode_number, offset;
        struct squashfs_symlink_inode *sym;
        struct squashfs_ldir_inode *ldir;
        struct squashfs_dir_inode *dir;
@@ -442,6 +454,12 @@ static int sqfs_search_dir(struct squashfs_dir_stream *dirs, char **token_list,
        struct fs_dirent *dent;
        unsigned char *table;
 
+       res = NULL;
+       rem = NULL;
+       path = NULL;
+       target = NULL;
+       sym_tokens = NULL;
+
        dirsp = (struct fs_dir_stream *)dirs;
 
        /* Start by root inode */
@@ -477,7 +495,8 @@ static int sqfs_search_dir(struct squashfs_dir_stream *dirs, char **token_list,
        for (j = 0; j < token_count; j++) {
                if (!sqfs_is_dir(get_unaligned_le16(&dir->inode_type))) {
                        printf("** Cannot find directory. **\n");
-                       return -EINVAL;
+                       ret = -EINVAL;
+                       goto out;
                }
 
                while (!sqfs_readdir(dirsp, &dent)) {
@@ -485,11 +504,13 @@ static int sqfs_search_dir(struct squashfs_dir_stream *dirs, char **token_list,
                        if (!ret)
                                break;
                        free(dirs->entry);
+                       dirs->entry = NULL;
                }
 
                if (ret) {
                        printf("** Cannot find directory. **\n");
-                       return -EINVAL;
+                       ret = -EINVAL;
+                       goto out;
                }
 
                /* Redefine inode as the found token */
@@ -506,38 +527,63 @@ static int sqfs_search_dir(struct squashfs_dir_stream *dirs, char **token_list,
                        sym = (struct squashfs_symlink_inode *)table;
                        /* Get first j + 1 tokens */
                        path = sqfs_concat_tokens(token_list, j + 1);
+                       if (!path) {
+                               ret = -ENOMEM;
+                               goto out;
+                       }
                        /* Resolve for these tokens */
                        target = sqfs_resolve_symlink(sym, path);
+                       if (!target) {
+                               ret = -ENOMEM;
+                               goto out;
+                       }
                        /* Join remaining tokens */
                        rem = sqfs_concat_tokens(token_list + j + 1, token_count -
                                                 j - 1);
+                       if (!rem) {
+                               ret = -ENOMEM;
+                               goto out;
+                       }
                        /* Concatenate remaining tokens and symlink's target */
                        res = malloc(strlen(rem) + strlen(target) + 1);
+                       if (!res) {
+                               ret = -ENOMEM;
+                               goto out;
+                       }
                        strcpy(res, target);
                        res[strlen(target)] = '/';
                        strcpy(res + strlen(target) + 1, rem);
                        token_count = sqfs_count_tokens(res);
 
-                       if (token_count < 0)
-                               return -EINVAL;
+                       if (token_count < 0) {
+                               ret = -EINVAL;
+                               goto out;
+                       }
 
                        sym_tokens = malloc(token_count * sizeof(char *));
-                       if (!sym_tokens)
-                               return -EINVAL;
+                       if (!sym_tokens) {
+                               ret = -EINVAL;
+                               goto out;
+                       }
 
                        /* Fill tokens list */
                        ret = sqfs_tokenize(sym_tokens, token_count, res);
-                       if (ret)
-                               return -EINVAL;
+                       if (ret) {
+                               ret = -EINVAL;
+                               goto out;
+                       }
                        free(dirs->entry);
+                       dirs->entry = NULL;
 
                        ret = sqfs_search_dir(dirs, sym_tokens, token_count,
                                              m_list, m_count);
-                       return ret;
+                       goto out;
                } else if (!sqfs_is_dir(get_unaligned_le16(&dir->inode_type))) {
                        printf("** Cannot find directory. **\n");
                        free(dirs->entry);
-                       return -EINVAL;
+                       dirs->entry = NULL;
+                       ret = -EINVAL;
+                       goto out;
                }
 
                /* Check if it is an extended dir. */
@@ -556,7 +602,9 @@ static int sqfs_search_dir(struct squashfs_dir_stream *dirs, char **token_list,
                if (sqfs_is_empty_dir(table)) {
                        printf("Empty directory.\n");
                        free(dirs->entry);
-                       return SQFS_EMPTY_DIR;
+                       dirs->entry = NULL;
+                       ret = SQFS_EMPTY_DIR;
+                       goto out;
                }
 
                dirs->table += SQFS_DIR_HEADER_SIZE;
@@ -564,6 +612,7 @@ static int sqfs_search_dir(struct squashfs_dir_stream *dirs, char **token_list,
                dirs->entry_count = dirs->dir_header->count + 1;
                dirs->size -= SQFS_DIR_HEADER_SIZE;
                free(dirs->entry);
+               dirs->entry = NULL;
        }
 
        offset = sqfs_dir_offset(table, m_list, m_count);
@@ -574,7 +623,13 @@ static int sqfs_search_dir(struct squashfs_dir_stream *dirs, char **token_list,
        else
                memcpy(&dirs->i_ldir, ldir, sizeof(*ldir));
 
-       return 0;
+out:
+       free(res);
+       free(rem);
+       free(path);
+       free(target);
+       free(sym_tokens);
+       return ret;
 }
 
 /*
@@ -688,6 +743,7 @@ static int sqfs_read_inode_table(unsigned char **inode_table)
                                              src_table, src_len);
                        if (ret) {
                                free(*inode_table);
+                               *inode_table = NULL;
                                goto free_itb;
                        }
 
@@ -722,6 +778,8 @@ static int sqfs_read_directory_table(unsigned char **dir_table, u32 **pos_list)
        unsigned long dest_len = 0;
        bool compressed;
 
+       *dir_table = NULL;
+       *pos_list = NULL;
        /* DIRECTORY TABLE */
        table_size = get_unaligned_le64(&sblk->fragment_table_start) -
                get_unaligned_le64(&sblk->directory_table_start);
@@ -736,35 +794,31 @@ static int sqfs_read_directory_table(unsigned char **dir_table, u32 **pos_list)
                return -ENOMEM;
 
        if (sqfs_disk_read(start, n_blks, dtb) < 0)
-               goto free_dtb;
+               goto out;
 
        /* Parse directory table (metadata block) header */
        ret = sqfs_read_metablock(dtb, table_offset, &compressed, &src_len);
        if (ret)
-               goto free_dtb;
+               goto out;
 
        /* Calculate total size to store the whole decompressed table */
        metablks_count = sqfs_count_metablks(dtb, table_offset, table_size);
        if (metablks_count < 1)
-               goto free_dtb;
+               goto out;
 
        *dir_table = malloc(metablks_count * SQFS_METADATA_BLOCK_SIZE);
        if (!*dir_table)
-               goto free_dtb;
+               goto out;
 
        *pos_list = malloc(metablks_count * sizeof(u32));
-       if (!*pos_list) {
-               free(*dir_table);
-               goto free_dtb;
-       }
+       if (!*pos_list)
+               goto out;
 
        ret = sqfs_get_metablk_pos(*pos_list, dtb, table_offset,
                                   metablks_count);
        if (ret) {
                metablks_count = -1;
-               free(*dir_table);
-               free(*pos_list);
-               goto free_dtb;
+               goto out;
        }
 
        src_table = dtb + table_offset + SQFS_HEADER_SIZE;
@@ -780,8 +834,7 @@ static int sqfs_read_directory_table(unsigned char **dir_table, u32 **pos_list)
                                              &dest_len, src_table, src_len);
                        if (ret) {
                                metablks_count = -1;
-                               free(*dir_table);
-                               goto free_dtb;
+                               goto out;
                        }
 
                        if (dest_len < SQFS_METADATA_BLOCK_SIZE) {
@@ -803,7 +856,13 @@ static int sqfs_read_directory_table(unsigned char **dir_table, u32 **pos_list)
                src_table += src_len + SQFS_HEADER_SIZE;
        }
 
-free_dtb:
+out:
+       if (metablks_count < 1) {
+               free(*dir_table);
+               free(*pos_list);
+               *dir_table = NULL;
+               *pos_list = NULL;
+       }
        free(dtb);
 
        return metablks_count;
@@ -1027,26 +1086,28 @@ int sqfs_probe(struct blk_desc *fs_dev_desc, struct disk_partition *fs_partition
 
        ret = sqfs_read_sblk(&sblk);
        if (ret)
-               return ret;
+               goto error;
 
        /* Make sure it has a valid SquashFS magic number*/
        if (get_unaligned_le32(&sblk->s_magic) != SQFS_MAGIC_NUMBER) {
                printf("Bad magic number for SquashFS image.\n");
-               ctxt.cur_dev = NULL;
-               return -EINVAL;
+               ret = -EINVAL;
+               goto error;
        }
 
        ctxt.sblk = sblk;
 
        ret = sqfs_decompressor_init(&ctxt);
-
        if (ret) {
-               ctxt.cur_dev = NULL;
-               free(ctxt.sblk);
-               return -EINVAL;
+               goto error;
        }
 
        return 0;
+error:
+       ctxt.cur_dev = NULL;
+       free(ctxt.sblk);
+       ctxt.sblk = NULL;
+       return ret;
 }
 
 static char *sqfs_basename(char *path)
@@ -1247,8 +1308,8 @@ static int sqfs_get_lregfile_info(struct squashfs_lreg_inode *lreg,
 int sqfs_read(const char *filename, void *buf, loff_t offset, loff_t len,
              loff_t *actread)
 {
-       char *dir, *fragment_block, *datablock = NULL, *data_buffer = NULL;
-       char *fragment, *file, *resolved, *data;
+       char *dir = NULL, *fragment_block, *datablock = NULL, *data_buffer = NULL;
+       char *fragment = NULL, *file = NULL, *resolved, *data;
        u64 start, n_blks, table_size, data_offset, table_offset;
        int ret, j, i_number, datablk_count = 0;
        struct squashfs_super_block *sblk = ctxt.sblk;
@@ -1266,6 +1327,14 @@ int sqfs_read(const char *filename, void *buf, loff_t offset, loff_t len,
 
        *actread = 0;
 
+       if (offset) {
+               /*
+                * TODO: implement reading at an offset in file
+                */
+               printf("Error: reading at a specific offset in a squashfs file is not supported yet.\n");
+               return -EINVAL;
+       }
+
        /*
         * sqfs_opendir will uncompress inode and directory tables, and will
         * return a pointer to the directory that contains the requested file.
@@ -1273,8 +1342,7 @@ int sqfs_read(const char *filename, void *buf, loff_t offset, loff_t len,
        sqfs_split_path(&file, &dir, filename);
        ret = sqfs_opendir(dir, &dirsp);
        if (ret) {
-               sqfs_closedir(dirsp);
-               goto free_paths;
+               goto out;
        }
 
        dirs = (struct squashfs_dir_stream *)dirsp;
@@ -1286,14 +1354,14 @@ int sqfs_read(const char *filename, void *buf, loff_t offset, loff_t len,
                        break;
 
                free(dirs->entry);
+               dirs->entry = NULL;
        }
 
        if (ret) {
                printf("File not found.\n");
                *actread = 0;
-               sqfs_closedir(dirsp);
                ret = -ENOENT;
-               goto free_paths;
+               goto out;
        }
 
        i_number = dirs->dir_header->inode_number + dirs->entry->inode_offset;
@@ -1308,7 +1376,7 @@ int sqfs_read(const char *filename, void *buf, loff_t offset, loff_t len,
                                                      sblk->block_size);
                if (datablk_count < 0) {
                        ret = -EINVAL;
-                       goto free_paths;
+                       goto out;
                }
 
                memcpy(finfo.blk_sizes, ipos + sizeof(*reg),
@@ -1321,7 +1389,7 @@ int sqfs_read(const char *filename, void *buf, loff_t offset, loff_t len,
                                                       sblk->block_size);
                if (datablk_count < 0) {
                        ret = -EINVAL;
-                       goto free_paths;
+                       goto out;
                }
 
                memcpy(finfo.blk_sizes, ipos + sizeof(*lreg),
@@ -1333,7 +1401,7 @@ int sqfs_read(const char *filename, void *buf, loff_t offset, loff_t len,
                resolved = sqfs_resolve_symlink(symlink, filename);
                ret = sqfs_read(resolved, buf, offset, len, actread);
                free(resolved);
-               goto free_paths;
+               goto out;
        case SQFS_BLKDEV_TYPE:
        case SQFS_CHRDEV_TYPE:
        case SQFS_LBLKDEV_TYPE:
@@ -1345,17 +1413,19 @@ int sqfs_read(const char *filename, void *buf, loff_t offset, loff_t len,
        default:
                printf("Unsupported entry type\n");
                ret = -EINVAL;
-               goto free_paths;
+               goto out;
        }
 
        /* If the user specifies a length, check its sanity */
        if (len) {
                if (len > finfo.size) {
                        ret = -EINVAL;
-                       goto free_paths;
+                       goto out;
                }
 
                finfo.size = len;
+       } else {
+               len = finfo.size;
        }
 
        if (datablk_count) {
@@ -1363,7 +1433,7 @@ int sqfs_read(const char *filename, void *buf, loff_t offset, loff_t len,
                datablock = malloc(get_unaligned_le32(&sblk->block_size));
                if (!datablock) {
                        ret = -ENOMEM;
-                       goto free_paths;
+                       goto out;
                }
        }
 
@@ -1378,7 +1448,7 @@ int sqfs_read(const char *filename, void *buf, loff_t offset, loff_t len,
 
                if (!data_buffer) {
                        ret = -ENOMEM;
-                       goto free_datablk;
+                       goto out;
                }
 
                ret = sqfs_disk_read(start, n_blks, data_buffer);
@@ -1389,7 +1459,7 @@ int sqfs_read(const char *filename, void *buf, loff_t offset, loff_t len,
                         * image with mksquashfs's -b <block_size> option.
                         */
                        printf("Error: too many data blocks to be read.\n");
-                       goto free_buffer;
+                       goto out;
                }
 
                data = data_buffer + table_offset;
@@ -1400,26 +1470,32 @@ int sqfs_read(const char *filename, void *buf, loff_t offset, loff_t len,
                        ret = sqfs_decompress(&ctxt, datablock, &dest_len,
                                              data, table_size);
                        if (ret)
-                               goto free_buffer;
+                               goto out;
 
-                       memcpy(buf + offset + *actread, datablock, dest_len);
+                       if ((*actread + dest_len) > len)
+                               dest_len = len - *actread;
+                       memcpy(buf + *actread, datablock, dest_len);
                        *actread += dest_len;
                } else {
-                       memcpy(buf + offset + *actread, data, table_size);
+                       if ((*actread + table_size) > len)
+                               table_size = len - *actread;
+                       memcpy(buf + *actread, data, table_size);
                        *actread += table_size;
                }
 
                data_offset += table_size;
+               free(data_buffer);
+               data_buffer = NULL;
+               if (*actread >= len)
+                       break;
        }
 
-       free(finfo.blk_sizes);
-
        /*
         * There is no need to continue if the file is not fragmented.
         */
        if (!finfo.frag) {
                ret = 0;
-               goto free_buffer;
+               goto out;
        }
 
        start = frag_entry.start / ctxt.cur_dev->blksz;
@@ -1431,12 +1507,12 @@ int sqfs_read(const char *filename, void *buf, loff_t offset, loff_t len,
 
        if (!fragment) {
                ret = -ENOMEM;
-               goto free_buffer;
+               goto out;
        }
 
        ret = sqfs_disk_read(start, n_blks, fragment);
        if (ret < 0)
-               goto free_fragment;
+               goto out;
 
        /* File compressed and fragmented */
        if (finfo.frag && finfo.comp) {
@@ -1444,7 +1520,7 @@ int sqfs_read(const char *filename, void *buf, loff_t offset, loff_t len,
                fragment_block = malloc(dest_len);
                if (!fragment_block) {
                        ret = -ENOMEM;
-                       goto free_fragment;
+                       goto out;
                }
 
                ret = sqfs_decompress(&ctxt, fragment_block, &dest_len,
@@ -1452,10 +1528,10 @@ int sqfs_read(const char *filename, void *buf, loff_t offset, loff_t len,
                                      frag_entry.size);
                if (ret) {
                        free(fragment_block);
-                       goto free_fragment;
+                       goto out;
                }
 
-               for (j = offset + *actread; j < finfo.size; j++) {
+               for (j = *actread; j < finfo.size; j++) {
                        memcpy(buf + j, &fragment_block[finfo.offset + j], 1);
                        (*actread)++;
                }
@@ -1465,23 +1541,22 @@ int sqfs_read(const char *filename, void *buf, loff_t offset, loff_t len,
        } else if (finfo.frag && !finfo.comp) {
                fragment_block = (void *)fragment + table_offset;
 
-               for (j = offset + *actread; j < finfo.size; j++) {
+               for (j = *actread; j < finfo.size; j++) {
                        memcpy(buf + j, &fragment_block[finfo.offset + j], 1);
                        (*actread)++;
                }
        }
 
-free_fragment:
+out:
        free(fragment);
-free_buffer:
-       if (datablk_count)
+       if (datablk_count) {
                free(data_buffer);
-free_datablk:
-       if (datablk_count)
                free(datablock);
-free_paths:
+       }
        free(file);
        free(dir);
+       free(finfo.blk_sizes);
+       sqfs_closedir(dirsp);
 
        return ret;
 }
@@ -1507,7 +1582,6 @@ int sqfs_size(const char *filename, loff_t *size)
         */
        ret = sqfs_opendir(dir, &dirsp);
        if (ret) {
-               sqfs_closedir(dirsp);
                ret = -EINVAL;
                goto free_strings;
        }
@@ -1519,6 +1593,7 @@ int sqfs_size(const char *filename, loff_t *size)
                if (!ret)
                        break;
                free(dirs->entry);
+               dirs->entry = NULL;
        }
 
        if (ret) {
@@ -1532,6 +1607,7 @@ int sqfs_size(const char *filename, loff_t *size)
        ipos = sqfs_find_inode(dirs->inode_table, i_number, sblk->inodes,
                               sblk->block_size);
        free(dirs->entry);
+       dirs->entry = NULL;
 
        base = (struct squashfs_base_inode *)ipos;
        switch (get_unaligned_le16(&base->inode_type)) {
@@ -1574,11 +1650,50 @@ free_strings:
        return ret;
 }
 
+int sqfs_exists(const char *filename)
+{
+       struct fs_dir_stream *dirsp = NULL;
+       struct squashfs_dir_stream *dirs;
+       char *dir, *file;
+       struct fs_dirent *dent;
+       int ret;
+
+       sqfs_split_path(&file, &dir, filename);
+       /*
+        * sqfs_opendir will uncompress inode and directory tables, and will
+        * return a pointer to the directory that contains the requested file.
+        */
+       ret = sqfs_opendir(dir, &dirsp);
+       if (ret) {
+               ret = -EINVAL;
+               goto free_strings;
+       }
+
+       dirs = (struct squashfs_dir_stream *)dirsp;
+
+       while (!sqfs_readdir(dirsp, &dent)) {
+               ret = strcmp(dent->name, file);
+               if (!ret)
+                       break;
+               free(dirs->entry);
+               dirs->entry = NULL;
+       }
+
+       sqfs_closedir(dirsp);
+
+free_strings:
+       free(dir);
+       free(file);
+
+       return ret == 0;
+}
+
 void sqfs_close(void)
 {
+       sqfs_decompressor_cleanup(&ctxt);
        free(ctxt.sblk);
+       ctxt.sblk = NULL;
        ctxt.cur_dev = NULL;
-       sqfs_decompressor_cleanup(&ctxt);
 }
 
 void sqfs_closedir(struct fs_dir_stream *dirs)