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