1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright (C) 2020 Bootlin
4 *
5 * Author: Joao Marcos Costa <joaomarcos.costa@bootlin.com>
6 *
7 * sqfs.c: SquashFS filesystem implementation
8 */
9
10#include <asm/unaligned.h>
11#include <div64.h>
12#include <errno.h>
13#include <fs.h>
14#include <linux/types.h>
15#include <asm/byteorder.h>
16#include <linux/compat.h>
17#include <memalign.h>
18#include <stdlib.h>
19#include <string.h>
20#include <squashfs.h>
21#include <part.h>
22
23#include "sqfs_decompressor.h"
24#include "sqfs_filesystem.h"
25#include "sqfs_utils.h"
26
27static struct squashfs_ctxt ctxt;
28
29static int sqfs_disk_read(__u32 block, __u32 nr_blocks, void *buf)
30{
31	ulong ret;
32
33	if (!ctxt.cur_dev)
34		return -1;
35
36	ret = blk_dread(ctxt.cur_dev, ctxt.cur_part_info.start + block,
37			nr_blocks, buf);
38
39	if (ret != nr_blocks)
40		return -1;
41
42	return ret;
43}
44
45static int sqfs_read_sblk(struct squashfs_super_block **sblk)
46{
47	*sblk = malloc_cache_aligned(ctxt.cur_dev->blksz);
48	if (!*sblk)
49		return -ENOMEM;
50
51	if (sqfs_disk_read(0, 1, *sblk) != 1) {
52		free(*sblk);
53		*sblk = NULL;
54		return -EINVAL;
55	}
56
57	return 0;
58}
59
60static int sqfs_count_tokens(const char *filename)
61{
62	int token_count = 1, l;
63
64	for (l = 1; l < strlen(filename); l++) {
65		if (filename[l] == '/')
66			token_count++;
67	}
68
69	/* Ignore trailing '/' in path */
70	if (filename[strlen(filename) - 1] == '/')
71		token_count--;
72
73	if (!token_count)
74		token_count = 1;
75
76	return token_count;
77}
78
79/*
80 * Calculates how many blocks are needed for the buffer used in sqfs_disk_read.
81 * The memory section (e.g. inode table) start offset and its end (i.e. the next
82 * table start) must be specified. It also calculates the offset from which to
83 * start reading the buffer.
84 */
85static int sqfs_calc_n_blks(__le64 start, __le64 end, u64 *offset)
86{
87	u64 start_, table_size;
88
89	table_size = le64_to_cpu(end) - le64_to_cpu(start);
90	start_ = lldiv(le64_to_cpu(start), ctxt.cur_dev->blksz);
91	*offset = le64_to_cpu(start) - (start_ * ctxt.cur_dev->blksz);
92
93	return DIV_ROUND_UP(table_size + *offset, ctxt.cur_dev->blksz);
94}
95
96/*
97 * Retrieves fragment block entry and returns true if the fragment block is
98 * compressed
99 */
100static int sqfs_frag_lookup(u32 inode_fragment_index,
101			    struct squashfs_fragment_block_entry *e)
102{
103	u64 start, end, exp_tbl, n_blks, src_len, table_offset, start_block;
104	unsigned char *metadata_buffer, *metadata, *table;
105	struct squashfs_fragment_block_entry *entries;
106	struct squashfs_super_block *sblk = ctxt.sblk;
107	unsigned long dest_len;
108	int block, offset, ret;
109	u16 header;
110
111	metadata_buffer = NULL;
112	entries = NULL;
113	table = NULL;
114
115	if (inode_fragment_index >= get_unaligned_le32(&sblk->fragments))
116		return -EINVAL;
117
118	start = get_unaligned_le64(&sblk->fragment_table_start);
119	end = get_unaligned_le64(&sblk->id_table_start);
120	exp_tbl = get_unaligned_le64(&sblk->export_table_start);
121
122	if (exp_tbl > start && exp_tbl < end)
123		end = exp_tbl;
124
125	n_blks = sqfs_calc_n_blks(sblk->fragment_table_start,
126				  cpu_to_le64(end), &table_offset);
127
128	start /= ctxt.cur_dev->blksz;
129
130	/* Allocate a proper sized buffer to store the fragment index table */
131	table = malloc_cache_aligned(n_blks * ctxt.cur_dev->blksz);
132	if (!table) {
133		ret = -ENOMEM;
134		goto out;
135	}
136
137	if (sqfs_disk_read(start, n_blks, table) < 0) {
138		ret = -EINVAL;
139		goto out;
140	}
141
142	block = SQFS_FRAGMENT_INDEX(inode_fragment_index);
143	offset = SQFS_FRAGMENT_INDEX_OFFSET(inode_fragment_index);
144
145	/*
146	 * Get the start offset of the metadata block that contains the right
147	 * fragment block entry
148	 */
149	start_block = get_unaligned_le64(table + table_offset + block *
150					 sizeof(u64));
151
152	start = start_block / ctxt.cur_dev->blksz;
153	n_blks = sqfs_calc_n_blks(cpu_to_le64(start_block),
154				  sblk->fragment_table_start, &table_offset);
155
156	metadata_buffer = malloc_cache_aligned(n_blks * ctxt.cur_dev->blksz);
157	if (!metadata_buffer) {
158		ret = -ENOMEM;
159		goto out;
160	}
161
162	if (sqfs_disk_read(start, n_blks, metadata_buffer) < 0) {
163		ret = -EINVAL;
164		goto out;
165	}
166
167	/* Every metadata block starts with a 16-bit header */
168	header = get_unaligned_le16(metadata_buffer + table_offset);
169	metadata = metadata_buffer + table_offset + SQFS_HEADER_SIZE;
170
171	if (!metadata || !header) {
172		ret = -ENOMEM;
173		goto out;
174	}
175
176	entries = malloc(SQFS_METADATA_BLOCK_SIZE);
177	if (!entries) {
178		ret = -ENOMEM;
179		goto out;
180	}
181
182	if (SQFS_COMPRESSED_METADATA(header)) {
183		src_len = SQFS_METADATA_SIZE(header);
184		dest_len = SQFS_METADATA_BLOCK_SIZE;
185		ret = sqfs_decompress(&ctxt, entries, &dest_len, metadata,
186				      src_len);
187		if (ret) {
188			ret = -EINVAL;
189			goto out;
190		}
191	} else {
192		memcpy(entries, metadata, SQFS_METADATA_SIZE(header));
193	}
194
195	*e = entries[offset];
196	ret = SQFS_COMPRESSED_BLOCK(e->size);
197
198out:
199	free(entries);
200	free(metadata_buffer);
201	free(table);
202
203	return ret;
204}
205
206/*
207 * The entry name is a flexible array member, and we don't know its size before
208 * actually reading the entry. So we need a first copy to retrieve this size so
209 * we can finally copy the whole struct.
210 */
211static int sqfs_read_entry(struct squashfs_directory_entry **dest, void *src)
212{
213	struct squashfs_directory_entry *tmp;
214	u16 sz;
215
216	tmp = src;
217	sz = get_unaligned_le16(src + sizeof(*tmp) - sizeof(u16));
218	/*
219	 * 'src' points to the begin of a directory entry, and 'sz' gets its
220	 * 'name_size' member's value. name_size is actually the string
221	 * length - 1, so adding 2 compensates this difference and adds space
222	 * for the trailling null byte.
223	 */
224	*dest = malloc(sizeof(*tmp) + sz + 2);
225	if (!*dest)
226		return -ENOMEM;
227
228	memcpy(*dest, src, sizeof(*tmp) + sz + 1);
229	(*dest)->name[sz + 1] = '\0';
230
231	return 0;
232}
233
234static int sqfs_get_tokens_length(char **tokens, int count)
235{
236	int length = 0, i;
237
238	/*
239	 * 1 is added to the result of strlen to consider the slash separator
240	 * between the tokens.
241	 */
242	for (i = 0; i < count; i++)
243		length += strlen(tokens[i]) + 1;
244
245	return length;
246}
247
248/* Takes a token list and returns a single string with '/' as separator. */
249static char *sqfs_concat_tokens(char **token_list, int token_count)
250{
251	char *result;
252	int i, length = 0, offset = 0;
253
254	length = sqfs_get_tokens_length(token_list, token_count);
255
256	result = malloc(length + 1);
257	if (!result)
258		return NULL;
259
260	result[length] = '\0';
261
262	for (i = 0; i < token_count; i++) {
263		strcpy(result + offset, token_list[i]);
264		offset += strlen(token_list[i]);
265		result[offset++] = '/';
266	}
267
268	return result;
269}
270
271/*
272 * Differently from sqfs_concat_tokens, sqfs_join writes the result into a
273 * previously allocated string, and returns the number of bytes written.
274 */
275static int sqfs_join(char **strings, char *dest, int start, int end,
276		     char separator)
277{
278	int i, offset = 0;
279
280	for (i = start; i < end; i++) {
281		strcpy(dest + offset, strings[i]);
282		offset += strlen(strings[i]);
283		if (i < end - 1)
284			dest[offset++] = separator;
285	}
286
287	return offset;
288}
289
290/*
291 * Fills the given token list using its size (count) and a source string (str)
292 */
293static int sqfs_tokenize(char **tokens, int count, const char *str)
294{
295	int i, j, ret = 0;
296	char *aux, *strc;
297
298	strc = strdup(str);
299	if (!strc)
300		return -ENOMEM;
301
302	if (!strcmp(strc, "/")) {
303		tokens[0] = strdup(strc);
304		if (!tokens[0]) {
305			ret = -ENOMEM;
306			goto free_strc;
307		}
308	} else {
309		for (j = 0; j < count; j++) {
310			aux = strtok(!j ? strc : NULL, "/");
311			tokens[j] = strdup(aux);
312			if (!tokens[j]) {
313				for (i = 0; i < j; i++)
314					free(tokens[i]);
315				ret = -ENOMEM;
316				goto free_strc;
317			}
318		}
319	}
320
321free_strc:
322	free(strc);
323
324	return ret;
325}
326
327/*
328 * Remove last 'updir + 1' tokens from the base path tokens list. This leaves us
329 * with a token list containing only the tokens needed to form the resolved
330 * path, and returns the decremented size of the token list.
331 */
332static int sqfs_clean_base_path(char **base, int count, int updir)
333{
334	int i;
335
336	for (i = count - updir - 1; i < count; i++)
337		free(base[i]);
338
339	return count - updir - 1;
340}
341
342/*
343 * Given the base ("current dir.") path and the relative one, generate the
344 * absolute path.
345 */
346static char *sqfs_get_abs_path(const char *base, const char *rel)
347{
348	char **base_tokens, **rel_tokens, *resolved = NULL;
349	int ret, bc, rc, i, updir = 0, resolved_size = 0, offset = 0;
350
351	base_tokens = NULL;
352	rel_tokens = NULL;
353
354	/* Memory allocation for the token lists */
355	bc = sqfs_count_tokens(base);
356	rc = sqfs_count_tokens(rel);
357	if (bc < 1 || rc < 1)
358		return NULL;
359
360	base_tokens = calloc(bc, sizeof(char *));
361	if (!base_tokens)
362		return NULL;
363
364	rel_tokens = calloc(rc, sizeof(char *));
365	if (!rel_tokens)
366		goto out;
367
368	/* Fill token lists */
369	ret = sqfs_tokenize(base_tokens, bc, base);
370	if (ret)
371		goto out;
372
373	ret = sqfs_tokenize(rel_tokens, rc, rel);
374	if (ret)
375		goto out;
376
377	/* count '..' occurrences in target path */
378	for (i = 0; i < rc; i++) {
379		if (!strcmp(rel_tokens[i], ".."))
380			updir++;
381	}
382
383	/* Remove the last token and the '..' occurrences */
384	bc = sqfs_clean_base_path(base_tokens, bc, updir);
385	if (bc < 0)
386		goto out;
387
388	/* Calculate resolved path size */
389	if (!bc)
390		resolved_size++;
391
392	resolved_size += sqfs_get_tokens_length(base_tokens, bc) +
393		sqfs_get_tokens_length(rel_tokens, rc);
394
395	resolved = malloc(resolved_size + 1);
396	if (!resolved)
397		goto out;
398
399	/* Set resolved path */
400	memset(resolved, '\0', resolved_size + 1);
401	offset += sqfs_join(base_tokens, resolved + offset, 0, bc, '/');
402	resolved[offset++] = '/';
403	offset += sqfs_join(rel_tokens, resolved + offset, updir, rc, '/');
404
405out:
406	if (rel_tokens)
407		for (i = 0; i < rc; i++)
408			free(rel_tokens[i]);
409	if (base_tokens)
410		for (i = 0; i < bc; i++)
411			free(base_tokens[i]);
412
413	free(rel_tokens);
414	free(base_tokens);
415
416	return resolved;
417}
418
419static char *sqfs_resolve_symlink(struct squashfs_symlink_inode *sym,
420				  const char *base_path)
421{
422	char *resolved, *target;
423	u32 sz;
424
425	sz = get_unaligned_le32(&sym->symlink_size);
426	target = malloc(sz + 1);
427	if (!target)
428		return NULL;
429
430	/*
431	 * There is no trailling null byte in the symlink's target path, so a
432	 * copy is made and a '\0' is added at its end.
433	 */
434	target[sz] = '\0';
435	/* Get target name (relative path) */
436	strncpy(target, sym->symlink, sz);
437
438	/* Relative -> absolute path conversion */
439	resolved = sqfs_get_abs_path(base_path, target);
440
441	free(target);
442
443	return resolved;
444}
445
446/*
447 * m_list contains each metadata block's position, and m_count is the number of
448 * elements of m_list. Those metadata blocks come from the compressed directory
449 * table.
450 */
451static int sqfs_search_dir(struct squashfs_dir_stream *dirs, char **token_list,
452			   int token_count, u32 *m_list, int m_count)
453{
454	struct squashfs_super_block *sblk = ctxt.sblk;
455	char *path, *target, **sym_tokens, *res, *rem;
456	int j, ret = 0, new_inode_number, offset;
457	struct squashfs_symlink_inode *sym;
458	struct squashfs_ldir_inode *ldir;
459	struct squashfs_dir_inode *dir;
460	struct fs_dir_stream *dirsp;
461	struct fs_dirent *dent;
462	unsigned char *table;
463
464	res = NULL;
465	rem = NULL;
466	path = NULL;
467	target = NULL;
468	sym_tokens = NULL;
469
470	dirsp = (struct fs_dir_stream *)dirs;
471
472	/* Start by root inode */
473	table = sqfs_find_inode(dirs->inode_table, le32_to_cpu(sblk->inodes),
474				sblk->inodes, sblk->block_size);
475
476	dir = (struct squashfs_dir_inode *)table;
477	ldir = (struct squashfs_ldir_inode *)table;
478
479	/* get directory offset in directory table */
480	offset = sqfs_dir_offset(table, m_list, m_count);
481	dirs->table = &dirs->dir_table[offset];
482
483	/* Setup directory header */
484	dirs->dir_header = malloc(SQFS_DIR_HEADER_SIZE);
485	if (!dirs->dir_header)
486		return -ENOMEM;
487
488	memcpy(dirs->dir_header, dirs->table, SQFS_DIR_HEADER_SIZE);
489
490	/* Initialize squashfs_dir_stream members */
491	dirs->table += SQFS_DIR_HEADER_SIZE;
492	dirs->size = get_unaligned_le16(&dir->file_size) - SQFS_DIR_HEADER_SIZE;
493	dirs->entry_count = dirs->dir_header->count + 1;
494
495	/* No path given -> root directory */
496	if (!strcmp(token_list[0], "/")) {
497		dirs->table = &dirs->dir_table[offset];
498		memcpy(&dirs->i_dir, dir, sizeof(*dir));
499		return 0;
500	}
501
502	for (j = 0; j < token_count; j++) {
503		if (!sqfs_is_dir(get_unaligned_le16(&dir->inode_type))) {
504			printf("** Cannot find directory. **\n");
505			ret = -EINVAL;
506			goto out;
507		}
508
509		while (!sqfs_readdir(dirsp, &dent)) {
510			ret = strcmp(dent->name, token_list[j]);
511			if (!ret)
512				break;
513			free(dirs->entry);
514			dirs->entry = NULL;
515		}
516
517		if (ret) {
518			printf("** Cannot find directory. **\n");
519			ret = -EINVAL;
520			goto out;
521		}
522
523		/* Redefine inode as the found token */
524		new_inode_number = dirs->entry->inode_offset +
525			dirs->dir_header->inode_number;
526
527		/* Get reference to inode in the inode table */
528		table = sqfs_find_inode(dirs->inode_table, new_inode_number,
529					sblk->inodes, sblk->block_size);
530		dir = (struct squashfs_dir_inode *)table;
531
532		/* Check for symbolic link and inode type sanity */
533		if (get_unaligned_le16(&dir->inode_type) == SQFS_SYMLINK_TYPE) {
534			sym = (struct squashfs_symlink_inode *)table;
535			/* Get first j + 1 tokens */
536			path = sqfs_concat_tokens(token_list, j + 1);
537			if (!path) {
538				ret = -ENOMEM;
539				goto out;
540			}
541			/* Resolve for these tokens */
542			target = sqfs_resolve_symlink(sym, path);
543			if (!target) {
544				ret = -ENOMEM;
545				goto out;
546			}
547			/* Join remaining tokens */
548			rem = sqfs_concat_tokens(token_list + j + 1, token_count -
549						 j - 1);
550			if (!rem) {
551				ret = -ENOMEM;
552				goto out;
553			}
554			/* Concatenate remaining tokens and symlink's target */
555			res = malloc(strlen(rem) + strlen(target) + 1);
556			if (!res) {
557				ret = -ENOMEM;
558				goto out;
559			}
560			strcpy(res, target);
561			res[strlen(target)] = '/';
562			strcpy(res + strlen(target) + 1, rem);
563			token_count = sqfs_count_tokens(res);
564
565			if (token_count < 0) {
566				ret = -EINVAL;
567				goto out;
568			}
569
570			sym_tokens = malloc(token_count * sizeof(char *));
571			if (!sym_tokens) {
572				ret = -EINVAL;
573				goto out;
574			}
575
576			/* Fill tokens list */
577			ret = sqfs_tokenize(sym_tokens, token_count, res);
578			if (ret) {
579				ret = -EINVAL;
580				goto out;
581			}
582			free(dirs->entry);
583			dirs->entry = NULL;
584
585			ret = sqfs_search_dir(dirs, sym_tokens, token_count,
586					      m_list, m_count);
587			goto out;
588		} else if (!sqfs_is_dir(get_unaligned_le16(&dir->inode_type))) {
589			printf("** Cannot find directory. **\n");
590			free(dirs->entry);
591			dirs->entry = NULL;
592			ret = -EINVAL;
593			goto out;
594		}
595
596		/* Check if it is an extended dir. */
597		if (get_unaligned_le16(&dir->inode_type) == SQFS_LDIR_TYPE)
598			ldir = (struct squashfs_ldir_inode *)table;
599
600		/* Get dir. offset into the directory table */
601		offset = sqfs_dir_offset(table, m_list, m_count);
602		dirs->table = &dirs->dir_table[offset];
603
604		/* Copy directory header */
605		memcpy(dirs->dir_header, &dirs->dir_table[offset],
606		       SQFS_DIR_HEADER_SIZE);
607
608		/* Check for empty directory */
609		if (sqfs_is_empty_dir(table)) {
610			printf("Empty directory.\n");
611			free(dirs->entry);
612			dirs->entry = NULL;
613			ret = SQFS_EMPTY_DIR;
614			goto out;
615		}
616
617		dirs->table += SQFS_DIR_HEADER_SIZE;
618		dirs->size = get_unaligned_le16(&dir->file_size);
619		dirs->entry_count = dirs->dir_header->count + 1;
620		dirs->size -= SQFS_DIR_HEADER_SIZE;
621		free(dirs->entry);
622		dirs->entry = NULL;
623	}
624
625	offset = sqfs_dir_offset(table, m_list, m_count);
626	dirs->table = &dirs->dir_table[offset];
627
628	if (get_unaligned_le16(&dir->inode_type) == SQFS_DIR_TYPE)
629		memcpy(&dirs->i_dir, dir, sizeof(*dir));
630	else
631		memcpy(&dirs->i_ldir, ldir, sizeof(*ldir));
632
633out:
634	free(res);
635	free(rem);
636	free(path);
637	free(target);
638	free(sym_tokens);
639	return ret;
640}
641
642/*
643 * Inode and directory tables are stored as a series of metadata blocks, and
644 * given the compressed size of this table, we can calculate how much metadata
645 * blocks are needed to store the result of the decompression, since a
646 * decompressed metadata block should have a size of 8KiB.
647 */
648static int sqfs_count_metablks(void *table, u32 offset, int table_size)
649{
650	int count = 0, cur_size = 0, ret;
651	u32 data_size;
652	bool comp;
653
654	do {
655		ret = sqfs_read_metablock(table, offset + cur_size, &comp,
656					  &data_size);
657		if (ret)
658			return -EINVAL;
659		cur_size += data_size + SQFS_HEADER_SIZE;
660		count++;
661	} while (cur_size < table_size);
662
663	return count;
664}
665
666/*
667 * Storing the metadata blocks header's positions will be useful while looking
668 * for an entry in the directory table, using the reference (index and offset)
669 * given by its inode.
670 */
671static int sqfs_get_metablk_pos(u32 *pos_list, void *table, u32 offset,
672				int metablks_count)
673{
674	u32 data_size, cur_size = 0;
675	int j, ret = 0;
676	bool comp;
677
678	if (!metablks_count)
679		return -EINVAL;
680
681	for (j = 0; j < metablks_count; j++) {
682		ret = sqfs_read_metablock(table, offset + cur_size, &comp,
683					  &data_size);
684		if (ret)
685			return -EINVAL;
686
687		cur_size += data_size + SQFS_HEADER_SIZE;
688		pos_list[j] = cur_size;
689	}
690
691	return ret;
692}
693
694static int sqfs_read_inode_table(unsigned char **inode_table)
695{
696	struct squashfs_super_block *sblk = ctxt.sblk;
697	u64 start, n_blks, table_offset, table_size;
698	int j, ret = 0, metablks_count;
699	unsigned char *src_table, *itb;
700	u32 src_len, dest_offset = 0;
701	unsigned long dest_len = 0;
702	bool compressed;
703
704	table_size = get_unaligned_le64(&sblk->directory_table_start) -
705		get_unaligned_le64(&sblk->inode_table_start);
706	start = get_unaligned_le64(&sblk->inode_table_start) /
707		ctxt.cur_dev->blksz;
708	n_blks = sqfs_calc_n_blks(sblk->inode_table_start,
709				  sblk->directory_table_start, &table_offset);
710
711	/* Allocate a proper sized buffer (itb) to store the inode table */
712	itb = malloc_cache_aligned(n_blks * ctxt.cur_dev->blksz);
713	if (!itb)
714		return -ENOMEM;
715
716	if (sqfs_disk_read(start, n_blks, itb) < 0) {
717		ret = -EINVAL;
718		goto free_itb;
719	}
720
721	/* Parse inode table (metadata block) header */
722	ret = sqfs_read_metablock(itb, table_offset, &compressed, &src_len);
723	if (ret) {
724		ret = -EINVAL;
725		goto free_itb;
726	}
727
728	/* Calculate size to store the whole decompressed table */
729	metablks_count = sqfs_count_metablks(itb, table_offset, table_size);
730	if (metablks_count < 1) {
731		ret = -EINVAL;
732		goto free_itb;
733	}
734
735	*inode_table = kcalloc(metablks_count, SQFS_METADATA_BLOCK_SIZE,
736			       GFP_KERNEL);
737	if (!*inode_table) {
738		ret = -ENOMEM;
739		printf("Error: failed to allocate squashfs inode_table of size %i, increasing CONFIG_SYS_MALLOC_LEN could help\n",
740		       metablks_count * SQFS_METADATA_BLOCK_SIZE);
741		goto free_itb;
742	}
743
744	src_table = itb + table_offset + SQFS_HEADER_SIZE;
745
746	/* Extract compressed Inode table */
747	for (j = 0; j < metablks_count; j++) {
748		sqfs_read_metablock(itb, table_offset, &compressed, &src_len);
749		if (compressed) {
750			dest_len = SQFS_METADATA_BLOCK_SIZE;
751			ret = sqfs_decompress(&ctxt, *inode_table +
752					      dest_offset, &dest_len,
753					      src_table, src_len);
754			if (ret) {
755				free(*inode_table);
756				*inode_table = NULL;
757				goto free_itb;
758			}
759
760			dest_offset += dest_len;
761		} else {
762			memcpy(*inode_table + (j * SQFS_METADATA_BLOCK_SIZE),
763			       src_table, src_len);
764		}
765
766		/*
767		 * Offsets to the decompression destination, to the metadata
768		 * buffer 'itb' and to the decompression source, respectively.
769		 */
770
771		table_offset += src_len + SQFS_HEADER_SIZE;
772		src_table += src_len + SQFS_HEADER_SIZE;
773	}
774
775free_itb:
776	free(itb);
777
778	return ret;
779}
780
781static int sqfs_read_directory_table(unsigned char **dir_table, u32 **pos_list)
782{
783	u64 start, n_blks, table_offset, table_size;
784	struct squashfs_super_block *sblk = ctxt.sblk;
785	int j, ret = 0, metablks_count = -1;
786	unsigned char *src_table, *dtb;
787	u32 src_len, dest_offset = 0;
788	unsigned long dest_len = 0;
789	bool compressed;
790
791	*dir_table = NULL;
792	*pos_list = NULL;
793	/* DIRECTORY TABLE */
794	table_size = get_unaligned_le64(&sblk->fragment_table_start) -
795		get_unaligned_le64(&sblk->directory_table_start);
796	start = get_unaligned_le64(&sblk->directory_table_start) /
797		ctxt.cur_dev->blksz;
798	n_blks = sqfs_calc_n_blks(sblk->directory_table_start,
799				  sblk->fragment_table_start, &table_offset);
800
801	/* Allocate a proper sized buffer (dtb) to store the directory table */
802	dtb = malloc_cache_aligned(n_blks * ctxt.cur_dev->blksz);
803	if (!dtb)
804		return -ENOMEM;
805
806	if (sqfs_disk_read(start, n_blks, dtb) < 0)
807		goto out;
808
809	/* Parse directory table (metadata block) header */
810	ret = sqfs_read_metablock(dtb, table_offset, &compressed, &src_len);
811	if (ret)
812		goto out;
813
814	/* Calculate total size to store the whole decompressed table */
815	metablks_count = sqfs_count_metablks(dtb, table_offset, table_size);
816	if (metablks_count < 1)
817		goto out;
818
819	*dir_table = malloc(metablks_count * SQFS_METADATA_BLOCK_SIZE);
820	if (!*dir_table)
821		goto out;
822
823	*pos_list = malloc(metablks_count * sizeof(u32));
824	if (!*pos_list)
825		goto out;
826
827	ret = sqfs_get_metablk_pos(*pos_list, dtb, table_offset,
828				   metablks_count);
829	if (ret) {
830		metablks_count = -1;
831		goto out;
832	}
833
834	src_table = dtb + table_offset + SQFS_HEADER_SIZE;
835
836	/* Extract compressed Directory table */
837	dest_offset = 0;
838	for (j = 0; j < metablks_count; j++) {
839		sqfs_read_metablock(dtb, table_offset, &compressed, &src_len);
840		if (compressed) {
841			dest_len = SQFS_METADATA_BLOCK_SIZE;
842			ret = sqfs_decompress(&ctxt, *dir_table +
843					      (j * SQFS_METADATA_BLOCK_SIZE),
844					      &dest_len, src_table, src_len);
845			if (ret) {
846				metablks_count = -1;
847				goto out;
848			}
849
850			if (dest_len < SQFS_METADATA_BLOCK_SIZE) {
851				dest_offset += dest_len;
852				break;
853			}
854
855			dest_offset += dest_len;
856		} else {
857			memcpy(*dir_table + (j * SQFS_METADATA_BLOCK_SIZE),
858			       src_table, src_len);
859		}
860
861		/*
862		 * Offsets to the decompression destination, to the metadata
863		 * buffer 'dtb' and to the decompression source, respectively.
864		 */
865		table_offset += src_len + SQFS_HEADER_SIZE;
866		src_table += src_len + SQFS_HEADER_SIZE;
867	}
868
869out:
870	if (metablks_count < 1) {
871		free(*dir_table);
872		free(*pos_list);
873		*dir_table = NULL;
874		*pos_list = NULL;
875	}
876	free(dtb);
877
878	return metablks_count;
879}
880
881int sqfs_opendir(const char *filename, struct fs_dir_stream **dirsp)
882{
883	unsigned char *inode_table = NULL, *dir_table = NULL;
884	int j, token_count = 0, ret = 0, metablks_count;
885	struct squashfs_dir_stream *dirs;
886	char **token_list = NULL, *path = NULL;
887	u32 *pos_list = NULL;
888
889	dirs = calloc(1, sizeof(*dirs));
890	if (!dirs)
891		return -EINVAL;
892
893	/* these should be set to NULL to prevent dangling pointers */
894	dirs->dir_header = NULL;
895	dirs->entry = NULL;
896	dirs->table = NULL;
897	dirs->inode_table = NULL;
898	dirs->dir_table = NULL;
899
900	ret = sqfs_read_inode_table(&inode_table);
901	if (ret) {
902		ret = -EINVAL;
903		goto out;
904	}
905
906	metablks_count = sqfs_read_directory_table(&dir_table, &pos_list);
907	if (metablks_count < 1) {
908		ret = -EINVAL;
909		goto out;
910	}
911
912	/* Tokenize filename */
913	token_count = sqfs_count_tokens(filename);
914	if (token_count < 0) {
915		ret = -EINVAL;
916		goto out;
917	}
918
919	path = strdup(filename);
920	if (!path) {
921		ret = -EINVAL;
922		goto out;
923	}
924
925	token_list = malloc(token_count * sizeof(char *));
926	if (!token_list) {
927		ret = -EINVAL;
928		goto out;
929	}
930
931	/* Fill tokens list */
932	ret = sqfs_tokenize(token_list, token_count, path);
933	if (ret)
934		goto out;
935	/*
936	 * ldir's (extended directory) size is greater than dir, so it works as
937	 * a general solution for the malloc size, since 'i' is a union.
938	 */
939	dirs->inode_table = inode_table;
940	dirs->dir_table = dir_table;
941	ret = sqfs_search_dir(dirs, token_list, token_count, pos_list,
942			      metablks_count);
943	if (ret)
944		goto out;
945
946	if (le16_to_cpu(dirs->i_dir.inode_type) == SQFS_DIR_TYPE)
947		dirs->size = le16_to_cpu(dirs->i_dir.file_size);
948	else
949		dirs->size = le32_to_cpu(dirs->i_ldir.file_size);
950
951	/* Setup directory header */
952	memcpy(dirs->dir_header, dirs->table, SQFS_DIR_HEADER_SIZE);
953	dirs->entry_count = dirs->dir_header->count + 1;
954	dirs->size -= SQFS_DIR_HEADER_SIZE;
955
956	/* Setup entry */
957	dirs->entry = NULL;
958	dirs->table += SQFS_DIR_HEADER_SIZE;
959
960	*dirsp = (struct fs_dir_stream *)dirs;
961
962out:
963	for (j = 0; j < token_count; j++)
964		free(token_list[j]);
965	free(token_list);
966	free(pos_list);
967	free(path);
968	if (ret) {
969		free(inode_table);
970		free(dirs);
971	}
972
973	return ret;
974}
975
976int sqfs_readdir(struct fs_dir_stream *fs_dirs, struct fs_dirent **dentp)
977{
978	struct squashfs_super_block *sblk = ctxt.sblk;
979	struct squashfs_dir_stream *dirs;
980	struct squashfs_lreg_inode *lreg;
981	struct squashfs_base_inode *base;
982	struct squashfs_reg_inode *reg;
983	int i_number, offset = 0, ret;
984	struct fs_dirent *dent;
985	unsigned char *ipos;
986	u16 name_size;
987
988	dirs = (struct squashfs_dir_stream *)fs_dirs;
989	if (!dirs->size) {
990		*dentp = NULL;
991		return -SQFS_STOP_READDIR;
992	}
993
994	dent = &dirs->dentp;
995
996	if (!dirs->entry_count) {
997		if (dirs->size > SQFS_DIR_HEADER_SIZE) {
998			dirs->size -= SQFS_DIR_HEADER_SIZE;
999		} else {
1000			*dentp = NULL;
1001			dirs->size = 0;
1002			return -SQFS_STOP_READDIR;
1003		}
1004
1005		if (dirs->size > SQFS_EMPTY_FILE_SIZE) {
1006			/* Read follow-up (emitted) dir. header */
1007			memcpy(dirs->dir_header, dirs->table,
1008			       SQFS_DIR_HEADER_SIZE);
1009			dirs->entry_count = dirs->dir_header->count + 1;
1010			ret = sqfs_read_entry(&dirs->entry, dirs->table +
1011					      SQFS_DIR_HEADER_SIZE);
1012			if (ret)
1013				return -SQFS_STOP_READDIR;
1014
1015			dirs->table += SQFS_DIR_HEADER_SIZE;
1016		}
1017	} else {
1018		ret = sqfs_read_entry(&dirs->entry, dirs->table);
1019		if (ret)
1020			return -SQFS_STOP_READDIR;
1021	}
1022
1023	i_number = dirs->dir_header->inode_number + dirs->entry->inode_offset;
1024	ipos = sqfs_find_inode(dirs->inode_table, i_number, sblk->inodes,
1025			       sblk->block_size);
1026
1027	base = (struct squashfs_base_inode *)ipos;
1028
1029	/* Set entry type and size */
1030	switch (dirs->entry->type) {
1031	case SQFS_DIR_TYPE:
1032	case SQFS_LDIR_TYPE:
1033		dent->type = FS_DT_DIR;
1034		break;
1035	case SQFS_REG_TYPE:
1036	case SQFS_LREG_TYPE:
1037		/*
1038		 * Entries do not differentiate extended from regular types, so
1039		 * it needs to be verified manually.
1040		 */
1041		if (get_unaligned_le16(&base->inode_type) == SQFS_LREG_TYPE) {
1042			lreg = (struct squashfs_lreg_inode *)ipos;
1043			dent->size = get_unaligned_le64(&lreg->file_size);
1044		} else {
1045			reg = (struct squashfs_reg_inode *)ipos;
1046			dent->size = get_unaligned_le32(&reg->file_size);
1047		}
1048
1049		dent->type = FS_DT_REG;
1050		break;
1051	case SQFS_BLKDEV_TYPE:
1052	case SQFS_CHRDEV_TYPE:
1053	case SQFS_LBLKDEV_TYPE:
1054	case SQFS_LCHRDEV_TYPE:
1055	case SQFS_FIFO_TYPE:
1056	case SQFS_SOCKET_TYPE:
1057	case SQFS_LFIFO_TYPE:
1058	case SQFS_LSOCKET_TYPE:
1059		dent->type = SQFS_MISC_ENTRY_TYPE;
1060		break;
1061	case SQFS_SYMLINK_TYPE:
1062	case SQFS_LSYMLINK_TYPE:
1063		dent->type = FS_DT_LNK;
1064		break;
1065	default:
1066		return -SQFS_STOP_READDIR;
1067	}
1068
1069	/* Set entry name (capped at FS_DIRENT_NAME_LEN which is a U-Boot limitation) */
1070	name_size = min_t(u16, dirs->entry->name_size + 1, FS_DIRENT_NAME_LEN - 1);
1071	strncpy(dent->name, dirs->entry->name, name_size);
1072	dent->name[name_size] = '\0';
1073
1074	offset = dirs->entry->name_size + 1 + SQFS_ENTRY_BASE_LENGTH;
1075	dirs->entry_count--;
1076
1077	/* Decrement size to be read */
1078	if (dirs->size > offset)
1079		dirs->size -= offset;
1080	else
1081		dirs->size = 0;
1082
1083	/* Keep a reference to the current entry before incrementing it */
1084	dirs->table += offset;
1085
1086	*dentp = dent;
1087
1088	return 0;
1089}
1090
1091int sqfs_probe(struct blk_desc *fs_dev_desc, struct disk_partition *fs_partition)
1092{
1093	struct squashfs_super_block *sblk;
1094	int ret;
1095
1096	ctxt.cur_dev = fs_dev_desc;
1097	ctxt.cur_part_info = *fs_partition;
1098
1099	ret = sqfs_read_sblk(&sblk);
1100	if (ret)
1101		goto error;
1102
1103	/* Make sure it has a valid SquashFS magic number*/
1104	if (get_unaligned_le32(&sblk->s_magic) != SQFS_MAGIC_NUMBER) {
1105		debug("Bad magic number for SquashFS image.\n");
1106		ret = -EINVAL;
1107		goto error;
1108	}
1109
1110	ctxt.sblk = sblk;
1111
1112	ret = sqfs_decompressor_init(&ctxt);
1113	if (ret) {
1114		goto error;
1115	}
1116
1117	return 0;
1118error:
1119	ctxt.cur_dev = NULL;
1120	free(ctxt.sblk);
1121	ctxt.sblk = NULL;
1122	return ret;
1123}
1124
1125static char *sqfs_basename(char *path)
1126{
1127	char *fname;
1128
1129	fname = path + strlen(path) - 1;
1130	while (fname >= path) {
1131		if (*fname == '/') {
1132			fname++;
1133			break;
1134		}
1135
1136		fname--;
1137	}
1138
1139	return fname;
1140}
1141
1142static char *sqfs_dirname(char *path)
1143{
1144	char *fname;
1145
1146	fname = sqfs_basename(path);
1147	--fname;
1148	*fname = '\0';
1149
1150	return path;
1151}
1152
1153/*
1154 * Takes a path to file and splits it in two parts: the filename itself and the
1155 * directory's path, e.g.:
1156 * path: /path/to/file.txt
1157 * file: file.txt
1158 * dir: /path/to
1159 */
1160static int sqfs_split_path(char **file, char **dir, const char *path)
1161{
1162	char *dirc, *basec, *bname, *dname, *tmp_path;
1163	int ret = 0;
1164
1165	*file = NULL;
1166	*dir = NULL;
1167	dirc = NULL;
1168	basec = NULL;
1169	bname = NULL;
1170	dname = NULL;
1171	tmp_path = NULL;
1172
1173	/* check for first slash in path*/
1174	if (path[0] == '/') {
1175		tmp_path = strdup(path);
1176		if (!tmp_path) {
1177			ret = -ENOMEM;
1178			goto out;
1179		}
1180	} else {
1181		tmp_path = malloc(strlen(path) + 2);
1182		if (!tmp_path) {
1183			ret = -ENOMEM;
1184			goto out;
1185		}
1186		tmp_path[0] = '/';
1187		strcpy(tmp_path + 1, path);
1188	}
1189
1190	/* String duplicates */
1191	dirc = strdup(tmp_path);
1192	if (!dirc) {
1193		ret = -ENOMEM;
1194		goto out;
1195	}
1196
1197	basec = strdup(tmp_path);
1198	if (!basec) {
1199		ret = -ENOMEM;
1200		goto out;
1201	}
1202
1203	dname = sqfs_dirname(dirc);
1204	bname = sqfs_basename(basec);
1205
1206	*file = strdup(bname);
1207
1208	if (!*file) {
1209		ret = -ENOMEM;
1210		goto out;
1211	}
1212
1213	if (*dname == '\0') {
1214		*dir = malloc(2);
1215		if (!*dir) {
1216			ret = -ENOMEM;
1217			goto out;
1218		}
1219
1220		(*dir)[0] = '/';
1221		(*dir)[1] = '\0';
1222	} else {
1223		*dir = strdup(dname);
1224		if (!*dir) {
1225			ret = -ENOMEM;
1226			goto out;
1227		}
1228	}
1229
1230out:
1231	if (ret) {
1232		free(*file);
1233		free(*dir);
1234		*dir = NULL;
1235		*file = NULL;
1236	}
1237	free(basec);
1238	free(dirc);
1239	free(tmp_path);
1240
1241	return ret;
1242}
1243
1244static int sqfs_get_regfile_info(struct squashfs_reg_inode *reg,
1245				 struct squashfs_file_info *finfo,
1246				 struct squashfs_fragment_block_entry *fentry,
1247				 __le32 blksz)
1248{
1249	int datablk_count = 0, ret;
1250
1251	finfo->size = get_unaligned_le32(&reg->file_size);
1252	finfo->offset = get_unaligned_le32(&reg->offset);
1253	finfo->start = get_unaligned_le32(&reg->start_block);
1254	finfo->frag = SQFS_IS_FRAGMENTED(get_unaligned_le32(&reg->fragment));
1255
1256	if (finfo->frag && finfo->offset == 0xFFFFFFFF)
1257		return -EINVAL;
1258
1259	if (finfo->size < 1 || finfo->start == 0xFFFFFFFF)
1260		return -EINVAL;
1261
1262	if (finfo->frag) {
1263		datablk_count = finfo->size / le32_to_cpu(blksz);
1264		ret = sqfs_frag_lookup(get_unaligned_le32(&reg->fragment),
1265				       fentry);
1266		if (ret < 0)
1267			return -EINVAL;
1268		finfo->comp = ret;
1269		if (fentry->size < 1 || fentry->start == 0x7FFFFFFF)
1270			return -EINVAL;
1271	} else {
1272		datablk_count = DIV_ROUND_UP(finfo->size, le32_to_cpu(blksz));
1273	}
1274
1275	finfo->blk_sizes = malloc(datablk_count * sizeof(u32));
1276	if (!finfo->blk_sizes)
1277		return -ENOMEM;
1278
1279	return datablk_count;
1280}
1281
1282static int sqfs_get_lregfile_info(struct squashfs_lreg_inode *lreg,
1283				  struct squashfs_file_info *finfo,
1284				  struct squashfs_fragment_block_entry *fentry,
1285				 __le32 blksz)
1286{
1287	int datablk_count = 0, ret;
1288
1289	finfo->size = get_unaligned_le64(&lreg->file_size);
1290	finfo->offset = get_unaligned_le32(&lreg->offset);
1291	finfo->start = get_unaligned_le64(&lreg->start_block);
1292	finfo->frag = SQFS_IS_FRAGMENTED(get_unaligned_le32(&lreg->fragment));
1293
1294	if (finfo->frag && finfo->offset == 0xFFFFFFFF)
1295		return -EINVAL;
1296
1297	if (finfo->size < 1 || finfo->start == 0x7FFFFFFF)
1298		return -EINVAL;
1299
1300	if (finfo->frag) {
1301		datablk_count = finfo->size / le32_to_cpu(blksz);
1302		ret = sqfs_frag_lookup(get_unaligned_le32(&lreg->fragment),
1303				       fentry);
1304		if (ret < 0)
1305			return -EINVAL;
1306		finfo->comp = ret;
1307		if (fentry->size < 1 || fentry->start == 0x7FFFFFFF)
1308			return -EINVAL;
1309	} else {
1310		datablk_count = DIV_ROUND_UP(finfo->size, le32_to_cpu(blksz));
1311	}
1312
1313	finfo->blk_sizes = malloc(datablk_count * sizeof(u32));
1314	if (!finfo->blk_sizes)
1315		return -ENOMEM;
1316
1317	return datablk_count;
1318}
1319
1320int sqfs_read(const char *filename, void *buf, loff_t offset, loff_t len,
1321	      loff_t *actread)
1322{
1323	char *dir = NULL, *fragment_block, *datablock = NULL;
1324	char *fragment = NULL, *file = NULL, *resolved, *data;
1325	u64 start, n_blks, table_size, data_offset, table_offset, sparse_size;
1326	int ret, j, i_number, datablk_count = 0;
1327	struct squashfs_super_block *sblk = ctxt.sblk;
1328	struct squashfs_fragment_block_entry frag_entry;
1329	struct squashfs_file_info finfo = {0};
1330	struct squashfs_symlink_inode *symlink;
1331	struct fs_dir_stream *dirsp = NULL;
1332	struct squashfs_dir_stream *dirs;
1333	struct squashfs_lreg_inode *lreg;
1334	struct squashfs_base_inode *base;
1335	struct squashfs_reg_inode *reg;
1336	unsigned long dest_len;
1337	struct fs_dirent *dent;
1338	unsigned char *ipos;
1339
1340	*actread = 0;
1341
1342	if (offset) {
1343		/*
1344		 * TODO: implement reading at an offset in file
1345		 */
1346		printf("Error: reading at a specific offset in a squashfs file is not supported yet.\n");
1347		return -EINVAL;
1348	}
1349
1350	/*
1351	 * sqfs_opendir will uncompress inode and directory tables, and will
1352	 * return a pointer to the directory that contains the requested file.
1353	 */
1354	sqfs_split_path(&file, &dir, filename);
1355	ret = sqfs_opendir(dir, &dirsp);
1356	if (ret) {
1357		goto out;
1358	}
1359
1360	dirs = (struct squashfs_dir_stream *)dirsp;
1361
1362	/* For now, only regular files are able to be loaded */
1363	while (!sqfs_readdir(dirsp, &dent)) {
1364		ret = strcmp(dent->name, file);
1365		if (!ret)
1366			break;
1367
1368		free(dirs->entry);
1369		dirs->entry = NULL;
1370	}
1371
1372	if (ret) {
1373		printf("File not found.\n");
1374		*actread = 0;
1375		ret = -ENOENT;
1376		goto out;
1377	}
1378
1379	i_number = dirs->dir_header->inode_number + dirs->entry->inode_offset;
1380	ipos = sqfs_find_inode(dirs->inode_table, i_number, sblk->inodes,
1381			       sblk->block_size);
1382
1383	base = (struct squashfs_base_inode *)ipos;
1384	switch (get_unaligned_le16(&base->inode_type)) {
1385	case SQFS_REG_TYPE:
1386		reg = (struct squashfs_reg_inode *)ipos;
1387		datablk_count = sqfs_get_regfile_info(reg, &finfo, &frag_entry,
1388						      sblk->block_size);
1389		if (datablk_count < 0) {
1390			ret = -EINVAL;
1391			goto out;
1392		}
1393
1394		memcpy(finfo.blk_sizes, ipos + sizeof(*reg),
1395		       datablk_count * sizeof(u32));
1396		break;
1397	case SQFS_LREG_TYPE:
1398		lreg = (struct squashfs_lreg_inode *)ipos;
1399		datablk_count = sqfs_get_lregfile_info(lreg, &finfo,
1400						       &frag_entry,
1401						       sblk->block_size);
1402		if (datablk_count < 0) {
1403			ret = -EINVAL;
1404			goto out;
1405		}
1406
1407		memcpy(finfo.blk_sizes, ipos + sizeof(*lreg),
1408		       datablk_count * sizeof(u32));
1409		break;
1410	case SQFS_SYMLINK_TYPE:
1411	case SQFS_LSYMLINK_TYPE:
1412		symlink = (struct squashfs_symlink_inode *)ipos;
1413		resolved = sqfs_resolve_symlink(symlink, filename);
1414		ret = sqfs_read(resolved, buf, offset, len, actread);
1415		free(resolved);
1416		goto out;
1417	case SQFS_BLKDEV_TYPE:
1418	case SQFS_CHRDEV_TYPE:
1419	case SQFS_LBLKDEV_TYPE:
1420	case SQFS_LCHRDEV_TYPE:
1421	case SQFS_FIFO_TYPE:
1422	case SQFS_SOCKET_TYPE:
1423	case SQFS_LFIFO_TYPE:
1424	case SQFS_LSOCKET_TYPE:
1425	default:
1426		printf("Unsupported entry type\n");
1427		ret = -EINVAL;
1428		goto out;
1429	}
1430
1431	/* If the user specifies a length, check its sanity */
1432	if (len) {
1433		if (len > finfo.size) {
1434			ret = -EINVAL;
1435			goto out;
1436		}
1437
1438		finfo.size = len;
1439	} else {
1440		len = finfo.size;
1441	}
1442
1443	if (datablk_count) {
1444		data_offset = finfo.start;
1445		datablock = malloc(get_unaligned_le32(&sblk->block_size));
1446		if (!datablock) {
1447			ret = -ENOMEM;
1448			goto out;
1449		}
1450	}
1451
1452	for (j = 0; j < datablk_count; j++) {
1453		char *data_buffer;
1454
1455		start = lldiv(data_offset, ctxt.cur_dev->blksz);
1456		table_size = SQFS_BLOCK_SIZE(finfo.blk_sizes[j]);
1457		table_offset = data_offset - (start * ctxt.cur_dev->blksz);
1458		n_blks = DIV_ROUND_UP(table_size + table_offset,
1459				      ctxt.cur_dev->blksz);
1460
1461		/* Don't load any data for sparse blocks */
1462		if (finfo.blk_sizes[j] == 0) {
1463			n_blks = 0;
1464			table_offset = 0;
1465			data_buffer = NULL;
1466			data = NULL;
1467		} else {
1468			data_buffer = malloc_cache_aligned(n_blks * ctxt.cur_dev->blksz);
1469
1470			if (!data_buffer) {
1471				ret = -ENOMEM;
1472				goto out;
1473			}
1474
1475			ret = sqfs_disk_read(start, n_blks, data_buffer);
1476			if (ret < 0) {
1477				/*
1478				 * Possible causes: too many data blocks or too large
1479				 * SquashFS block size. Tip: re-compile the SquashFS
1480				 * image with mksquashfs's -b <block_size> option.
1481				 */
1482				printf("Error: too many data blocks to be read.\n");
1483				goto out;
1484			}
1485
1486			data = data_buffer + table_offset;
1487		}
1488
1489		/* Load the data */
1490		if (finfo.blk_sizes[j] == 0) {
1491			/* This is a sparse block */
1492			sparse_size = get_unaligned_le32(&sblk->block_size);
1493			if ((*actread + sparse_size) > len)
1494				sparse_size = len - *actread;
1495			memset(buf + *actread, 0, sparse_size);
1496			*actread += sparse_size;
1497		} else if (SQFS_COMPRESSED_BLOCK(finfo.blk_sizes[j])) {
1498			dest_len = get_unaligned_le32(&sblk->block_size);
1499			ret = sqfs_decompress(&ctxt, datablock, &dest_len,
1500					      data, table_size);
1501			if (ret)
1502				goto out;
1503
1504			if ((*actread + dest_len) > len)
1505				dest_len = len - *actread;
1506			memcpy(buf + *actread, datablock, dest_len);
1507			*actread += dest_len;
1508		} else {
1509			if ((*actread + table_size) > len)
1510				table_size = len - *actread;
1511			memcpy(buf + *actread, data, table_size);
1512			*actread += table_size;
1513		}
1514
1515		data_offset += table_size;
1516		free(data_buffer);
1517		if (*actread >= len)
1518			break;
1519	}
1520
1521	/*
1522	 * There is no need to continue if the file is not fragmented.
1523	 */
1524	if (!finfo.frag) {
1525		ret = 0;
1526		goto out;
1527	}
1528
1529	start = lldiv(frag_entry.start, ctxt.cur_dev->blksz);
1530	table_size = SQFS_BLOCK_SIZE(frag_entry.size);
1531	table_offset = frag_entry.start - (start * ctxt.cur_dev->blksz);
1532	n_blks = DIV_ROUND_UP(table_size + table_offset, ctxt.cur_dev->blksz);
1533
1534	fragment = malloc_cache_aligned(n_blks * ctxt.cur_dev->blksz);
1535
1536	if (!fragment) {
1537		ret = -ENOMEM;
1538		goto out;
1539	}
1540
1541	ret = sqfs_disk_read(start, n_blks, fragment);
1542	if (ret < 0)
1543		goto out;
1544
1545	/* File compressed and fragmented */
1546	if (finfo.frag && finfo.comp) {
1547		dest_len = get_unaligned_le32(&sblk->block_size);
1548		fragment_block = malloc(dest_len);
1549		if (!fragment_block) {
1550			ret = -ENOMEM;
1551			goto out;
1552		}
1553
1554		ret = sqfs_decompress(&ctxt, fragment_block, &dest_len,
1555				      (void *)fragment  + table_offset,
1556				      frag_entry.size);
1557		if (ret) {
1558			free(fragment_block);
1559			goto out;
1560		}
1561
1562		memcpy(buf + *actread, &fragment_block[finfo.offset], finfo.size - *actread);
1563		*actread = finfo.size;
1564
1565		free(fragment_block);
1566
1567	} else if (finfo.frag && !finfo.comp) {
1568		fragment_block = (void *)fragment + table_offset;
1569
1570		memcpy(buf + *actread, &fragment_block[finfo.offset], finfo.size - *actread);
1571		*actread = finfo.size;
1572	}
1573
1574out:
1575	free(fragment);
1576	free(datablock);
1577	free(file);
1578	free(dir);
1579	free(finfo.blk_sizes);
1580	sqfs_closedir(dirsp);
1581
1582	return ret;
1583}
1584
1585int sqfs_size(const char *filename, loff_t *size)
1586{
1587	struct squashfs_super_block *sblk = ctxt.sblk;
1588	struct squashfs_symlink_inode *symlink;
1589	struct fs_dir_stream *dirsp = NULL;
1590	struct squashfs_base_inode *base;
1591	struct squashfs_dir_stream *dirs;
1592	struct squashfs_lreg_inode *lreg;
1593	struct squashfs_reg_inode *reg;
1594	char *dir, *file, *resolved;
1595	struct fs_dirent *dent;
1596	unsigned char *ipos;
1597	int ret, i_number;
1598
1599	sqfs_split_path(&file, &dir, filename);
1600	/*
1601	 * sqfs_opendir will uncompress inode and directory tables, and will
1602	 * return a pointer to the directory that contains the requested file.
1603	 */
1604	ret = sqfs_opendir(dir, &dirsp);
1605	if (ret) {
1606		ret = -EINVAL;
1607		goto free_strings;
1608	}
1609
1610	dirs = (struct squashfs_dir_stream *)dirsp;
1611
1612	while (!sqfs_readdir(dirsp, &dent)) {
1613		ret = strcmp(dent->name, file);
1614		if (!ret)
1615			break;
1616		free(dirs->entry);
1617		dirs->entry = NULL;
1618	}
1619
1620	if (ret) {
1621		printf("File not found.\n");
1622		*size = 0;
1623		ret = -EINVAL;
1624		goto free_strings;
1625	}
1626
1627	i_number = dirs->dir_header->inode_number + dirs->entry->inode_offset;
1628	ipos = sqfs_find_inode(dirs->inode_table, i_number, sblk->inodes,
1629			       sblk->block_size);
1630	free(dirs->entry);
1631	dirs->entry = NULL;
1632
1633	base = (struct squashfs_base_inode *)ipos;
1634	switch (get_unaligned_le16(&base->inode_type)) {
1635	case SQFS_REG_TYPE:
1636		reg = (struct squashfs_reg_inode *)ipos;
1637		*size = get_unaligned_le32(&reg->file_size);
1638		break;
1639	case SQFS_LREG_TYPE:
1640		lreg = (struct squashfs_lreg_inode *)ipos;
1641		*size = get_unaligned_le64(&lreg->file_size);
1642		break;
1643	case SQFS_SYMLINK_TYPE:
1644	case SQFS_LSYMLINK_TYPE:
1645		symlink = (struct squashfs_symlink_inode *)ipos;
1646		resolved = sqfs_resolve_symlink(symlink, filename);
1647		ret = sqfs_size(resolved, size);
1648		free(resolved);
1649		break;
1650	case SQFS_BLKDEV_TYPE:
1651	case SQFS_CHRDEV_TYPE:
1652	case SQFS_LBLKDEV_TYPE:
1653	case SQFS_LCHRDEV_TYPE:
1654	case SQFS_FIFO_TYPE:
1655	case SQFS_SOCKET_TYPE:
1656	case SQFS_LFIFO_TYPE:
1657	case SQFS_LSOCKET_TYPE:
1658	default:
1659		printf("Unable to recover entry's size.\n");
1660		*size = 0;
1661		ret = -EINVAL;
1662		break;
1663	}
1664
1665free_strings:
1666	free(dir);
1667	free(file);
1668
1669	sqfs_closedir(dirsp);
1670
1671	return ret;
1672}
1673
1674int sqfs_exists(const char *filename)
1675{
1676	struct fs_dir_stream *dirsp = NULL;
1677	struct squashfs_dir_stream *dirs;
1678	char *dir, *file;
1679	struct fs_dirent *dent;
1680	int ret;
1681
1682	sqfs_split_path(&file, &dir, filename);
1683	/*
1684	 * sqfs_opendir will uncompress inode and directory tables, and will
1685	 * return a pointer to the directory that contains the requested file.
1686	 */
1687	ret = sqfs_opendir(dir, &dirsp);
1688	if (ret) {
1689		ret = -EINVAL;
1690		goto free_strings;
1691	}
1692
1693	dirs = (struct squashfs_dir_stream *)dirsp;
1694
1695	while (!sqfs_readdir(dirsp, &dent)) {
1696		ret = strcmp(dent->name, file);
1697		if (!ret)
1698			break;
1699		free(dirs->entry);
1700		dirs->entry = NULL;
1701	}
1702
1703	sqfs_closedir(dirsp);
1704
1705free_strings:
1706	free(dir);
1707	free(file);
1708
1709	return ret == 0;
1710}
1711
1712void sqfs_close(void)
1713{
1714	sqfs_decompressor_cleanup(&ctxt);
1715	free(ctxt.sblk);
1716	ctxt.sblk = NULL;
1717	ctxt.cur_dev = NULL;
1718}
1719
1720void sqfs_closedir(struct fs_dir_stream *dirs)
1721{
1722	struct squashfs_dir_stream *sqfs_dirs;
1723
1724	if (!dirs)
1725		return;
1726
1727	sqfs_dirs = (struct squashfs_dir_stream *)dirs;
1728	free(sqfs_dirs->inode_table);
1729	free(sqfs_dirs->dir_table);
1730	free(sqfs_dirs->dir_header);
1731	free(sqfs_dirs);
1732}
1733