blob: 9c1f87caca10d607d8b7ff0d816c8963c52d56cf [file] [log] [blame]
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001// 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>
Sean Nyekjaera8eb9622022-05-12 20:37:14 +020011#include <div64.h>
Joao Marcos Costa29da3742020-07-30 15:33:47 +020012#include <errno.h>
13#include <fs.h>
14#include <linux/types.h>
Pali Rohár501073d2022-04-06 23:31:53 +020015#include <asm/byteorder.h>
Miquel Raynaledaf6042022-06-27 12:20:03 +020016#include <linux/compat.h>
Joao Marcos Costa29da3742020-07-30 15:33:47 +020017#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
Joao Marcos Costa29da3742020-07-30 15:33:47 +020027static 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);
Richard Genoud1d95a1e2020-11-24 18:07:52 +010053 sblk = NULL;
Joao Marcos Costa29da3742020-07-30 15:33:47 +020054 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_ = 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, 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;
Joao Marcos Costab87fd012020-08-18 17:17:22 +0200109 u16 header;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200110
Richard Genoud07726e12020-11-03 12:11:15 +0100111 metadata_buffer = NULL;
112 entries = NULL;
113 table = NULL;
114
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200115 if (inode_fragment_index >= get_unaligned_le32(&sblk->fragments))
116 return -EINVAL;
117
118 start = get_unaligned_le64(&sblk->fragment_table_start) /
119 ctxt.cur_dev->blksz;
120 n_blks = sqfs_calc_n_blks(sblk->fragment_table_start,
121 sblk->export_table_start,
122 &table_offset);
123
124 /* Allocate a proper sized buffer to store the fragment index table */
125 table = malloc_cache_aligned(n_blks * ctxt.cur_dev->blksz);
Richard Genoud07726e12020-11-03 12:11:15 +0100126 if (!table) {
127 ret = -ENOMEM;
128 goto out;
129 }
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200130
131 if (sqfs_disk_read(start, n_blks, table) < 0) {
Richard Genoud07726e12020-11-03 12:11:15 +0100132 ret = -EINVAL;
133 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200134 }
135
136 block = SQFS_FRAGMENT_INDEX(inode_fragment_index);
137 offset = SQFS_FRAGMENT_INDEX_OFFSET(inode_fragment_index);
138
139 /*
140 * Get the start offset of the metadata block that contains the right
141 * fragment block entry
142 */
143 start_block = get_unaligned_le64(table + table_offset + block *
144 sizeof(u64));
145
146 start = start_block / ctxt.cur_dev->blksz;
147 n_blks = sqfs_calc_n_blks(cpu_to_le64(start_block),
148 sblk->fragment_table_start, &table_offset);
149
150 metadata_buffer = malloc_cache_aligned(n_blks * ctxt.cur_dev->blksz);
151 if (!metadata_buffer) {
152 ret = -ENOMEM;
Richard Genoud07726e12020-11-03 12:11:15 +0100153 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200154 }
155
156 if (sqfs_disk_read(start, n_blks, metadata_buffer) < 0) {
157 ret = -EINVAL;
Richard Genoud07726e12020-11-03 12:11:15 +0100158 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200159 }
160
161 /* Every metadata block starts with a 16-bit header */
162 header = get_unaligned_le16(metadata_buffer + table_offset);
163 metadata = metadata_buffer + table_offset + SQFS_HEADER_SIZE;
164
Joao Marcos Costaf1dfe4a2020-09-11 12:21:06 +0200165 if (!metadata || !header) {
Joao Marcos Costab7fb3c32020-08-19 18:28:41 +0200166 ret = -ENOMEM;
Richard Genoud07726e12020-11-03 12:11:15 +0100167 goto out;
Joao Marcos Costab7fb3c32020-08-19 18:28:41 +0200168 }
169
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200170 entries = malloc(SQFS_METADATA_BLOCK_SIZE);
171 if (!entries) {
172 ret = -ENOMEM;
Richard Genoud07726e12020-11-03 12:11:15 +0100173 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200174 }
175
176 if (SQFS_COMPRESSED_METADATA(header)) {
177 src_len = SQFS_METADATA_SIZE(header);
178 dest_len = SQFS_METADATA_BLOCK_SIZE;
Joao Marcos Costab87fd012020-08-18 17:17:22 +0200179 ret = sqfs_decompress(&ctxt, entries, &dest_len, metadata,
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200180 src_len);
181 if (ret) {
182 ret = -EINVAL;
Richard Genoud07726e12020-11-03 12:11:15 +0100183 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200184 }
185 } else {
186 memcpy(entries, metadata, SQFS_METADATA_SIZE(header));
187 }
188
189 *e = entries[offset];
190 ret = SQFS_COMPRESSED_BLOCK(e->size);
191
Richard Genoud07726e12020-11-03 12:11:15 +0100192out:
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200193 free(entries);
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200194 free(metadata_buffer);
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200195 free(table);
196
197 return ret;
198}
199
200/*
201 * The entry name is a flexible array member, and we don't know its size before
202 * actually reading the entry. So we need a first copy to retrieve this size so
203 * we can finally copy the whole struct.
204 */
205static int sqfs_read_entry(struct squashfs_directory_entry **dest, void *src)
206{
207 struct squashfs_directory_entry *tmp;
208 u16 sz;
209
210 tmp = src;
211 sz = get_unaligned_le16(src + sizeof(*tmp) - sizeof(u16));
212 /*
213 * 'src' points to the begin of a directory entry, and 'sz' gets its
214 * 'name_size' member's value. name_size is actually the string
215 * length - 1, so adding 2 compensates this difference and adds space
216 * for the trailling null byte.
217 */
218 *dest = malloc(sizeof(*tmp) + sz + 2);
219 if (!*dest)
220 return -ENOMEM;
221
222 memcpy(*dest, src, sizeof(*tmp) + sz + 1);
223 (*dest)->name[sz + 1] = '\0';
224
225 return 0;
226}
227
228static int sqfs_get_tokens_length(char **tokens, int count)
229{
230 int length = 0, i;
231
232 /*
233 * 1 is added to the result of strlen to consider the slash separator
234 * between the tokens.
235 */
236 for (i = 0; i < count; i++)
237 length += strlen(tokens[i]) + 1;
238
239 return length;
240}
241
242/* Takes a token list and returns a single string with '/' as separator. */
243static char *sqfs_concat_tokens(char **token_list, int token_count)
244{
245 char *result;
246 int i, length = 0, offset = 0;
247
248 length = sqfs_get_tokens_length(token_list, token_count);
249
250 result = malloc(length + 1);
Richard Genoud489e7ae2020-11-03 12:11:08 +0100251 if (!result)
252 return NULL;
253
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200254 result[length] = '\0';
255
256 for (i = 0; i < token_count; i++) {
257 strcpy(result + offset, token_list[i]);
258 offset += strlen(token_list[i]);
259 result[offset++] = '/';
260 }
261
262 return result;
263}
264
265/*
266 * Differently from sqfs_concat_tokens, sqfs_join writes the result into a
267 * previously allocated string, and returns the number of bytes written.
268 */
269static int sqfs_join(char **strings, char *dest, int start, int end,
270 char separator)
271{
272 int i, offset = 0;
273
274 for (i = start; i < end; i++) {
275 strcpy(dest + offset, strings[i]);
276 offset += strlen(strings[i]);
277 if (i < end - 1)
278 dest[offset++] = separator;
279 }
280
281 return offset;
282}
283
284/*
285 * Fills the given token list using its size (count) and a source string (str)
286 */
287static int sqfs_tokenize(char **tokens, int count, const char *str)
288{
Joao Marcos Costab7fb3c32020-08-19 18:28:41 +0200289 int i, j, ret = 0;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200290 char *aux, *strc;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200291
292 strc = strdup(str);
293 if (!strc)
294 return -ENOMEM;
295
296 if (!strcmp(strc, "/")) {
297 tokens[0] = strdup(strc);
298 if (!tokens[0]) {
Joao Marcos Costab7fb3c32020-08-19 18:28:41 +0200299 ret = -ENOMEM;
300 goto free_strc;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200301 }
302 } else {
303 for (j = 0; j < count; j++) {
304 aux = strtok(!j ? strc : NULL, "/");
305 tokens[j] = strdup(aux);
306 if (!tokens[j]) {
307 for (i = 0; i < j; i++)
308 free(tokens[i]);
Joao Marcos Costab7fb3c32020-08-19 18:28:41 +0200309 ret = -ENOMEM;
310 goto free_strc;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200311 }
312 }
313 }
314
Joao Marcos Costab7fb3c32020-08-19 18:28:41 +0200315free_strc:
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200316 free(strc);
317
Joao Marcos Costab7fb3c32020-08-19 18:28:41 +0200318 return ret;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200319}
320
321/*
322 * Remove last 'updir + 1' tokens from the base path tokens list. This leaves us
323 * with a token list containing only the tokens needed to form the resolved
324 * path, and returns the decremented size of the token list.
325 */
326static int sqfs_clean_base_path(char **base, int count, int updir)
327{
328 int i;
329
330 for (i = count - updir - 1; i < count; i++)
331 free(base[i]);
332
333 return count - updir - 1;
334}
335
336/*
337 * Given the base ("current dir.") path and the relative one, generate the
338 * absolute path.
339 */
340static char *sqfs_get_abs_path(const char *base, const char *rel)
341{
342 char **base_tokens, **rel_tokens, *resolved = NULL;
343 int ret, bc, rc, i, updir = 0, resolved_size = 0, offset = 0;
344
Richard Genoudcf9072a2020-11-03 12:11:17 +0100345 base_tokens = NULL;
346 rel_tokens = NULL;
347
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200348 /* Memory allocation for the token lists */
349 bc = sqfs_count_tokens(base);
350 rc = sqfs_count_tokens(rel);
351 if (bc < 1 || rc < 1)
352 return NULL;
353
Richard Genoudcf9072a2020-11-03 12:11:17 +0100354 base_tokens = calloc(bc, sizeof(char *));
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200355 if (!base_tokens)
356 return NULL;
357
Richard Genoudcf9072a2020-11-03 12:11:17 +0100358 rel_tokens = calloc(rc, sizeof(char *));
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200359 if (!rel_tokens)
Richard Genoudcf9072a2020-11-03 12:11:17 +0100360 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200361
362 /* Fill token lists */
363 ret = sqfs_tokenize(base_tokens, bc, base);
364 if (ret)
Richard Genoudcf9072a2020-11-03 12:11:17 +0100365 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200366
Richard Genoud9cb30362020-11-03 12:11:16 +0100367 ret = sqfs_tokenize(rel_tokens, rc, rel);
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200368 if (ret)
Richard Genoudcf9072a2020-11-03 12:11:17 +0100369 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200370
371 /* count '..' occurrences in target path */
372 for (i = 0; i < rc; i++) {
373 if (!strcmp(rel_tokens[i], ".."))
374 updir++;
375 }
376
377 /* Remove the last token and the '..' occurrences */
378 bc = sqfs_clean_base_path(base_tokens, bc, updir);
379 if (bc < 0)
Richard Genoudcf9072a2020-11-03 12:11:17 +0100380 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200381
382 /* Calculate resolved path size */
383 if (!bc)
384 resolved_size++;
385
386 resolved_size += sqfs_get_tokens_length(base_tokens, bc) +
387 sqfs_get_tokens_length(rel_tokens, rc);
388
389 resolved = malloc(resolved_size + 1);
390 if (!resolved)
Richard Genoudcf9072a2020-11-03 12:11:17 +0100391 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200392
393 /* Set resolved path */
394 memset(resolved, '\0', resolved_size + 1);
395 offset += sqfs_join(base_tokens, resolved + offset, 0, bc, '/');
396 resolved[offset++] = '/';
397 offset += sqfs_join(rel_tokens, resolved + offset, updir, rc, '/');
398
Richard Genoudcf9072a2020-11-03 12:11:17 +0100399out:
400 if (rel_tokens)
401 for (i = 0; i < rc; i++)
402 free(rel_tokens[i]);
403 if (base_tokens)
404 for (i = 0; i < bc; i++)
405 free(base_tokens[i]);
406
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200407 free(rel_tokens);
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200408 free(base_tokens);
409
410 return resolved;
411}
412
413static char *sqfs_resolve_symlink(struct squashfs_symlink_inode *sym,
414 const char *base_path)
415{
416 char *resolved, *target;
417 u32 sz;
418
419 sz = get_unaligned_le32(&sym->symlink_size);
420 target = malloc(sz + 1);
421 if (!target)
422 return NULL;
423
424 /*
425 * There is no trailling null byte in the symlink's target path, so a
426 * copy is made and a '\0' is added at its end.
427 */
428 target[sz] = '\0';
429 /* Get target name (relative path) */
430 strncpy(target, sym->symlink, sz);
431
432 /* Relative -> absolute path conversion */
433 resolved = sqfs_get_abs_path(base_path, target);
434
435 free(target);
436
437 return resolved;
438}
439
440/*
441 * m_list contains each metadata block's position, and m_count is the number of
442 * elements of m_list. Those metadata blocks come from the compressed directory
443 * table.
444 */
445static int sqfs_search_dir(struct squashfs_dir_stream *dirs, char **token_list,
446 int token_count, u32 *m_list, int m_count)
447{
448 struct squashfs_super_block *sblk = ctxt.sblk;
449 char *path, *target, **sym_tokens, *res, *rem;
Richard Genoud2762f652020-11-03 12:11:06 +0100450 int j, ret = 0, new_inode_number, offset;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200451 struct squashfs_symlink_inode *sym;
Joao Marcos Costaf1dfe4a2020-09-11 12:21:06 +0200452 struct squashfs_ldir_inode *ldir;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200453 struct squashfs_dir_inode *dir;
454 struct fs_dir_stream *dirsp;
455 struct fs_dirent *dent;
456 unsigned char *table;
457
Richard Genoud2762f652020-11-03 12:11:06 +0100458 res = NULL;
459 rem = NULL;
460 path = NULL;
461 target = NULL;
462 sym_tokens = NULL;
463
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200464 dirsp = (struct fs_dir_stream *)dirs;
465
466 /* Start by root inode */
467 table = sqfs_find_inode(dirs->inode_table, le32_to_cpu(sblk->inodes),
468 sblk->inodes, sblk->block_size);
469
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200470 dir = (struct squashfs_dir_inode *)table;
Joao Marcos Costaf1dfe4a2020-09-11 12:21:06 +0200471 ldir = (struct squashfs_ldir_inode *)table;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200472
473 /* get directory offset in directory table */
474 offset = sqfs_dir_offset(table, m_list, m_count);
475 dirs->table = &dirs->dir_table[offset];
476
477 /* Setup directory header */
478 dirs->dir_header = malloc(SQFS_DIR_HEADER_SIZE);
479 if (!dirs->dir_header)
480 return -ENOMEM;
481
482 memcpy(dirs->dir_header, dirs->table, SQFS_DIR_HEADER_SIZE);
483
484 /* Initialize squashfs_dir_stream members */
485 dirs->table += SQFS_DIR_HEADER_SIZE;
486 dirs->size = get_unaligned_le16(&dir->file_size) - SQFS_DIR_HEADER_SIZE;
487 dirs->entry_count = dirs->dir_header->count + 1;
488
489 /* No path given -> root directory */
490 if (!strcmp(token_list[0], "/")) {
491 dirs->table = &dirs->dir_table[offset];
492 memcpy(&dirs->i_dir, dir, sizeof(*dir));
493 return 0;
494 }
495
496 for (j = 0; j < token_count; j++) {
497 if (!sqfs_is_dir(get_unaligned_le16(&dir->inode_type))) {
498 printf("** Cannot find directory. **\n");
Richard Genoud2762f652020-11-03 12:11:06 +0100499 ret = -EINVAL;
500 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200501 }
502
503 while (!sqfs_readdir(dirsp, &dent)) {
504 ret = strcmp(dent->name, token_list[j]);
505 if (!ret)
506 break;
507 free(dirs->entry);
Richard Genoude405fc42020-11-03 12:11:05 +0100508 dirs->entry = NULL;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200509 }
510
511 if (ret) {
512 printf("** Cannot find directory. **\n");
Richard Genoud2762f652020-11-03 12:11:06 +0100513 ret = -EINVAL;
514 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200515 }
516
517 /* Redefine inode as the found token */
518 new_inode_number = dirs->entry->inode_offset +
519 dirs->dir_header->inode_number;
520
521 /* Get reference to inode in the inode table */
522 table = sqfs_find_inode(dirs->inode_table, new_inode_number,
523 sblk->inodes, sblk->block_size);
524 dir = (struct squashfs_dir_inode *)table;
525
526 /* Check for symbolic link and inode type sanity */
527 if (get_unaligned_le16(&dir->inode_type) == SQFS_SYMLINK_TYPE) {
528 sym = (struct squashfs_symlink_inode *)table;
529 /* Get first j + 1 tokens */
530 path = sqfs_concat_tokens(token_list, j + 1);
Richard Genoud2762f652020-11-03 12:11:06 +0100531 if (!path) {
532 ret = -ENOMEM;
533 goto out;
534 }
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200535 /* Resolve for these tokens */
536 target = sqfs_resolve_symlink(sym, path);
Richard Genoud2762f652020-11-03 12:11:06 +0100537 if (!target) {
538 ret = -ENOMEM;
539 goto out;
540 }
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200541 /* Join remaining tokens */
542 rem = sqfs_concat_tokens(token_list + j + 1, token_count -
543 j - 1);
Richard Genoud2762f652020-11-03 12:11:06 +0100544 if (!rem) {
545 ret = -ENOMEM;
546 goto out;
547 }
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200548 /* Concatenate remaining tokens and symlink's target */
549 res = malloc(strlen(rem) + strlen(target) + 1);
Richard Genoud2762f652020-11-03 12:11:06 +0100550 if (!res) {
551 ret = -ENOMEM;
552 goto out;
553 }
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200554 strcpy(res, target);
555 res[strlen(target)] = '/';
556 strcpy(res + strlen(target) + 1, rem);
557 token_count = sqfs_count_tokens(res);
558
Richard Genoud2762f652020-11-03 12:11:06 +0100559 if (token_count < 0) {
560 ret = -EINVAL;
561 goto out;
562 }
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200563
564 sym_tokens = malloc(token_count * sizeof(char *));
Richard Genoud2762f652020-11-03 12:11:06 +0100565 if (!sym_tokens) {
566 ret = -EINVAL;
567 goto out;
568 }
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200569
570 /* Fill tokens list */
571 ret = sqfs_tokenize(sym_tokens, token_count, res);
Richard Genoud2762f652020-11-03 12:11:06 +0100572 if (ret) {
573 ret = -EINVAL;
574 goto out;
575 }
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200576 free(dirs->entry);
Richard Genoude405fc42020-11-03 12:11:05 +0100577 dirs->entry = NULL;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200578
579 ret = sqfs_search_dir(dirs, sym_tokens, token_count,
580 m_list, m_count);
Richard Genoud2762f652020-11-03 12:11:06 +0100581 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200582 } else if (!sqfs_is_dir(get_unaligned_le16(&dir->inode_type))) {
583 printf("** Cannot find directory. **\n");
584 free(dirs->entry);
Richard Genoude405fc42020-11-03 12:11:05 +0100585 dirs->entry = NULL;
Richard Genoud2762f652020-11-03 12:11:06 +0100586 ret = -EINVAL;
587 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200588 }
589
590 /* Check if it is an extended dir. */
591 if (get_unaligned_le16(&dir->inode_type) == SQFS_LDIR_TYPE)
592 ldir = (struct squashfs_ldir_inode *)table;
593
594 /* Get dir. offset into the directory table */
595 offset = sqfs_dir_offset(table, m_list, m_count);
596 dirs->table = &dirs->dir_table[offset];
597
598 /* Copy directory header */
599 memcpy(dirs->dir_header, &dirs->dir_table[offset],
600 SQFS_DIR_HEADER_SIZE);
601
602 /* Check for empty directory */
603 if (sqfs_is_empty_dir(table)) {
604 printf("Empty directory.\n");
605 free(dirs->entry);
Richard Genoude405fc42020-11-03 12:11:05 +0100606 dirs->entry = NULL;
Richard Genoud2762f652020-11-03 12:11:06 +0100607 ret = SQFS_EMPTY_DIR;
608 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200609 }
610
611 dirs->table += SQFS_DIR_HEADER_SIZE;
612 dirs->size = get_unaligned_le16(&dir->file_size);
613 dirs->entry_count = dirs->dir_header->count + 1;
614 dirs->size -= SQFS_DIR_HEADER_SIZE;
615 free(dirs->entry);
Richard Genoude405fc42020-11-03 12:11:05 +0100616 dirs->entry = NULL;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200617 }
618
619 offset = sqfs_dir_offset(table, m_list, m_count);
620 dirs->table = &dirs->dir_table[offset];
621
622 if (get_unaligned_le16(&dir->inode_type) == SQFS_DIR_TYPE)
623 memcpy(&dirs->i_dir, dir, sizeof(*dir));
624 else
625 memcpy(&dirs->i_ldir, ldir, sizeof(*ldir));
626
Richard Genoud2762f652020-11-03 12:11:06 +0100627out:
628 free(res);
629 free(rem);
630 free(path);
631 free(target);
632 free(sym_tokens);
633 return ret;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200634}
635
636/*
637 * Inode and directory tables are stored as a series of metadata blocks, and
638 * given the compressed size of this table, we can calculate how much metadata
639 * blocks are needed to store the result of the decompression, since a
640 * decompressed metadata block should have a size of 8KiB.
641 */
642static int sqfs_count_metablks(void *table, u32 offset, int table_size)
643{
644 int count = 0, cur_size = 0, ret;
645 u32 data_size;
646 bool comp;
647
648 do {
649 ret = sqfs_read_metablock(table, offset + cur_size, &comp,
650 &data_size);
651 if (ret)
652 return -EINVAL;
653 cur_size += data_size + SQFS_HEADER_SIZE;
654 count++;
655 } while (cur_size < table_size);
656
657 return count;
658}
659
660/*
661 * Storing the metadata blocks header's positions will be useful while looking
662 * for an entry in the directory table, using the reference (index and offset)
663 * given by its inode.
664 */
665static int sqfs_get_metablk_pos(u32 *pos_list, void *table, u32 offset,
666 int metablks_count)
667{
668 u32 data_size, cur_size = 0;
669 int j, ret = 0;
670 bool comp;
671
672 if (!metablks_count)
673 return -EINVAL;
674
675 for (j = 0; j < metablks_count; j++) {
676 ret = sqfs_read_metablock(table, offset + cur_size, &comp,
677 &data_size);
678 if (ret)
679 return -EINVAL;
680
681 cur_size += data_size + SQFS_HEADER_SIZE;
682 pos_list[j] = cur_size;
683 }
684
685 return ret;
686}
687
688static int sqfs_read_inode_table(unsigned char **inode_table)
689{
690 struct squashfs_super_block *sblk = ctxt.sblk;
691 u64 start, n_blks, table_offset, table_size;
Joao Marcos Costab87fd012020-08-18 17:17:22 +0200692 int j, ret = 0, metablks_count;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200693 unsigned char *src_table, *itb;
694 u32 src_len, dest_offset = 0;
Joao Marcos Costab7fb3c32020-08-19 18:28:41 +0200695 unsigned long dest_len = 0;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200696 bool compressed;
697
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200698 table_size = get_unaligned_le64(&sblk->directory_table_start) -
699 get_unaligned_le64(&sblk->inode_table_start);
700 start = get_unaligned_le64(&sblk->inode_table_start) /
701 ctxt.cur_dev->blksz;
702 n_blks = sqfs_calc_n_blks(sblk->inode_table_start,
703 sblk->directory_table_start, &table_offset);
704
705 /* Allocate a proper sized buffer (itb) to store the inode table */
706 itb = malloc_cache_aligned(n_blks * ctxt.cur_dev->blksz);
707 if (!itb)
708 return -ENOMEM;
709
710 if (sqfs_disk_read(start, n_blks, itb) < 0) {
711 ret = -EINVAL;
712 goto free_itb;
713 }
714
715 /* Parse inode table (metadata block) header */
716 ret = sqfs_read_metablock(itb, table_offset, &compressed, &src_len);
717 if (ret) {
718 ret = -EINVAL;
719 goto free_itb;
720 }
721
722 /* Calculate size to store the whole decompressed table */
723 metablks_count = sqfs_count_metablks(itb, table_offset, table_size);
724 if (metablks_count < 1) {
725 ret = -EINVAL;
726 goto free_itb;
727 }
728
Miquel Raynaledaf6042022-06-27 12:20:03 +0200729 *inode_table = kcalloc(metablks_count, SQFS_METADATA_BLOCK_SIZE,
730 GFP_KERNEL);
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200731 if (!*inode_table) {
732 ret = -ENOMEM;
Lars Weber205783f2022-01-13 14:28:45 +0100733 printf("Error: failed to allocate squashfs inode_table of size %i, increasing CONFIG_SYS_MALLOC_LEN could help\n",
734 metablks_count * SQFS_METADATA_BLOCK_SIZE);
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200735 goto free_itb;
736 }
737
738 src_table = itb + table_offset + SQFS_HEADER_SIZE;
739
740 /* Extract compressed Inode table */
741 for (j = 0; j < metablks_count; j++) {
742 sqfs_read_metablock(itb, table_offset, &compressed, &src_len);
743 if (compressed) {
744 dest_len = SQFS_METADATA_BLOCK_SIZE;
Joao Marcos Costab87fd012020-08-18 17:17:22 +0200745 ret = sqfs_decompress(&ctxt, *inode_table +
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200746 dest_offset, &dest_len,
747 src_table, src_len);
748 if (ret) {
749 free(*inode_table);
Richard Genouda62528d2020-11-03 12:11:07 +0100750 *inode_table = NULL;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200751 goto free_itb;
752 }
753
Joao Marcos Costab7fb3c32020-08-19 18:28:41 +0200754 dest_offset += dest_len;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200755 } else {
756 memcpy(*inode_table + (j * SQFS_METADATA_BLOCK_SIZE),
757 src_table, src_len);
758 }
759
760 /*
761 * Offsets to the decompression destination, to the metadata
762 * buffer 'itb' and to the decompression source, respectively.
763 */
Joao Marcos Costab7fb3c32020-08-19 18:28:41 +0200764
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200765 table_offset += src_len + SQFS_HEADER_SIZE;
766 src_table += src_len + SQFS_HEADER_SIZE;
767 }
768
769free_itb:
770 free(itb);
771
772 return ret;
773}
774
775static int sqfs_read_directory_table(unsigned char **dir_table, u32 **pos_list)
776{
777 u64 start, n_blks, table_offset, table_size;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200778 struct squashfs_super_block *sblk = ctxt.sblk;
Joao Marcos Costab87fd012020-08-18 17:17:22 +0200779 int j, ret = 0, metablks_count = -1;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200780 unsigned char *src_table, *dtb;
781 u32 src_len, dest_offset = 0;
Joao Marcos Costab7fb3c32020-08-19 18:28:41 +0200782 unsigned long dest_len = 0;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200783 bool compressed;
784
Richard Genoud2f9c9852020-11-03 12:11:04 +0100785 *dir_table = NULL;
786 *pos_list = NULL;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200787 /* DIRECTORY TABLE */
788 table_size = get_unaligned_le64(&sblk->fragment_table_start) -
789 get_unaligned_le64(&sblk->directory_table_start);
790 start = get_unaligned_le64(&sblk->directory_table_start) /
791 ctxt.cur_dev->blksz;
792 n_blks = sqfs_calc_n_blks(sblk->directory_table_start,
793 sblk->fragment_table_start, &table_offset);
794
795 /* Allocate a proper sized buffer (dtb) to store the directory table */
796 dtb = malloc_cache_aligned(n_blks * ctxt.cur_dev->blksz);
797 if (!dtb)
798 return -ENOMEM;
799
800 if (sqfs_disk_read(start, n_blks, dtb) < 0)
Richard Genoud2f9c9852020-11-03 12:11:04 +0100801 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200802
803 /* Parse directory table (metadata block) header */
804 ret = sqfs_read_metablock(dtb, table_offset, &compressed, &src_len);
805 if (ret)
Richard Genoud2f9c9852020-11-03 12:11:04 +0100806 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200807
808 /* Calculate total size to store the whole decompressed table */
809 metablks_count = sqfs_count_metablks(dtb, table_offset, table_size);
810 if (metablks_count < 1)
Richard Genoud2f9c9852020-11-03 12:11:04 +0100811 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200812
813 *dir_table = malloc(metablks_count * SQFS_METADATA_BLOCK_SIZE);
814 if (!*dir_table)
Richard Genoud2f9c9852020-11-03 12:11:04 +0100815 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200816
817 *pos_list = malloc(metablks_count * sizeof(u32));
Richard Genoud2f9c9852020-11-03 12:11:04 +0100818 if (!*pos_list)
819 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200820
821 ret = sqfs_get_metablk_pos(*pos_list, dtb, table_offset,
822 metablks_count);
823 if (ret) {
824 metablks_count = -1;
Richard Genoud2f9c9852020-11-03 12:11:04 +0100825 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200826 }
827
828 src_table = dtb + table_offset + SQFS_HEADER_SIZE;
829
830 /* Extract compressed Directory table */
831 dest_offset = 0;
832 for (j = 0; j < metablks_count; j++) {
833 sqfs_read_metablock(dtb, table_offset, &compressed, &src_len);
834 if (compressed) {
835 dest_len = SQFS_METADATA_BLOCK_SIZE;
Joao Marcos Costab87fd012020-08-18 17:17:22 +0200836 ret = sqfs_decompress(&ctxt, *dir_table +
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200837 (j * SQFS_METADATA_BLOCK_SIZE),
838 &dest_len, src_table, src_len);
839 if (ret) {
840 metablks_count = -1;
Richard Genoud2f9c9852020-11-03 12:11:04 +0100841 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200842 }
843
844 if (dest_len < SQFS_METADATA_BLOCK_SIZE) {
845 dest_offset += dest_len;
846 break;
847 }
Joao Marcos Costab7fb3c32020-08-19 18:28:41 +0200848
849 dest_offset += dest_len;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200850 } else {
851 memcpy(*dir_table + (j * SQFS_METADATA_BLOCK_SIZE),
852 src_table, src_len);
853 }
854
855 /*
856 * Offsets to the decompression destination, to the metadata
857 * buffer 'dtb' and to the decompression source, respectively.
858 */
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200859 table_offset += src_len + SQFS_HEADER_SIZE;
860 src_table += src_len + SQFS_HEADER_SIZE;
861 }
862
Richard Genoud2f9c9852020-11-03 12:11:04 +0100863out:
864 if (metablks_count < 1) {
865 free(*dir_table);
866 free(*pos_list);
867 *dir_table = NULL;
868 *pos_list = NULL;
869 }
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200870 free(dtb);
871
872 return metablks_count;
873}
874
875int sqfs_opendir(const char *filename, struct fs_dir_stream **dirsp)
876{
877 unsigned char *inode_table = NULL, *dir_table = NULL;
Richard Genoud32bea5b2020-11-03 12:11:01 +0100878 int j, token_count = 0, ret = 0, metablks_count;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200879 struct squashfs_dir_stream *dirs;
Richard Genoud32bea5b2020-11-03 12:11:01 +0100880 char **token_list = NULL, *path = NULL;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200881 u32 *pos_list = NULL;
882
Heinrich Schuchardte086af82021-05-17 08:21:39 +0200883 dirs = calloc(1, sizeof(*dirs));
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200884 if (!dirs)
885 return -EINVAL;
886
Richard Genoud557f08f2020-11-03 12:11:00 +0100887 /* these should be set to NULL to prevent dangling pointers */
888 dirs->dir_header = NULL;
889 dirs->entry = NULL;
890 dirs->table = NULL;
891 dirs->inode_table = NULL;
892 dirs->dir_table = NULL;
893
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200894 ret = sqfs_read_inode_table(&inode_table);
Richard Genoud557f08f2020-11-03 12:11:00 +0100895 if (ret) {
896 ret = -EINVAL;
Richard Genoud32bea5b2020-11-03 12:11:01 +0100897 goto out;
Richard Genoud557f08f2020-11-03 12:11:00 +0100898 }
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200899
900 metablks_count = sqfs_read_directory_table(&dir_table, &pos_list);
Richard Genoud557f08f2020-11-03 12:11:00 +0100901 if (metablks_count < 1) {
902 ret = -EINVAL;
Richard Genoud32bea5b2020-11-03 12:11:01 +0100903 goto out;
Richard Genoud557f08f2020-11-03 12:11:00 +0100904 }
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200905
906 /* Tokenize filename */
907 token_count = sqfs_count_tokens(filename);
Richard Genoud557f08f2020-11-03 12:11:00 +0100908 if (token_count < 0) {
909 ret = -EINVAL;
Richard Genoud32bea5b2020-11-03 12:11:01 +0100910 goto out;
Richard Genoud557f08f2020-11-03 12:11:00 +0100911 }
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200912
913 path = strdup(filename);
Richard Genoud557f08f2020-11-03 12:11:00 +0100914 if (!path) {
915 ret = -EINVAL;
Richard Genoud32bea5b2020-11-03 12:11:01 +0100916 goto out;
Richard Genoud557f08f2020-11-03 12:11:00 +0100917 }
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200918
919 token_list = malloc(token_count * sizeof(char *));
920 if (!token_list) {
921 ret = -EINVAL;
Richard Genoud32bea5b2020-11-03 12:11:01 +0100922 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200923 }
924
925 /* Fill tokens list */
926 ret = sqfs_tokenize(token_list, token_count, path);
927 if (ret)
Richard Genoud32bea5b2020-11-03 12:11:01 +0100928 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200929 /*
930 * ldir's (extended directory) size is greater than dir, so it works as
931 * a general solution for the malloc size, since 'i' is a union.
932 */
933 dirs->inode_table = inode_table;
934 dirs->dir_table = dir_table;
935 ret = sqfs_search_dir(dirs, token_list, token_count, pos_list,
936 metablks_count);
937 if (ret)
Richard Genoud32bea5b2020-11-03 12:11:01 +0100938 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200939
940 if (le16_to_cpu(dirs->i_dir.inode_type) == SQFS_DIR_TYPE)
941 dirs->size = le16_to_cpu(dirs->i_dir.file_size);
942 else
943 dirs->size = le32_to_cpu(dirs->i_ldir.file_size);
944
945 /* Setup directory header */
946 memcpy(dirs->dir_header, dirs->table, SQFS_DIR_HEADER_SIZE);
947 dirs->entry_count = dirs->dir_header->count + 1;
948 dirs->size -= SQFS_DIR_HEADER_SIZE;
949
950 /* Setup entry */
951 dirs->entry = NULL;
952 dirs->table += SQFS_DIR_HEADER_SIZE;
953
954 *dirsp = (struct fs_dir_stream *)dirs;
955
Richard Genoud32bea5b2020-11-03 12:11:01 +0100956out:
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200957 for (j = 0; j < token_count; j++)
958 free(token_list[j]);
959 free(token_list);
960 free(pos_list);
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200961 free(path);
Richard Genoud32bea5b2020-11-03 12:11:01 +0100962 if (ret) {
Richard Genoud557f08f2020-11-03 12:11:00 +0100963 free(inode_table);
Richard Genoud557f08f2020-11-03 12:11:00 +0100964 free(dirs);
Richard Genoud32bea5b2020-11-03 12:11:01 +0100965 }
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200966
967 return ret;
968}
969
970int sqfs_readdir(struct fs_dir_stream *fs_dirs, struct fs_dirent **dentp)
971{
972 struct squashfs_super_block *sblk = ctxt.sblk;
973 struct squashfs_dir_stream *dirs;
974 struct squashfs_lreg_inode *lreg;
975 struct squashfs_base_inode *base;
976 struct squashfs_reg_inode *reg;
977 int i_number, offset = 0, ret;
978 struct fs_dirent *dent;
979 unsigned char *ipos;
Miquel Raynala8e95fd2022-06-09 16:02:06 +0200980 u16 name_size;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200981
982 dirs = (struct squashfs_dir_stream *)fs_dirs;
983 if (!dirs->size) {
984 *dentp = NULL;
985 return -SQFS_STOP_READDIR;
986 }
987
988 dent = &dirs->dentp;
989
990 if (!dirs->entry_count) {
991 if (dirs->size > SQFS_DIR_HEADER_SIZE) {
992 dirs->size -= SQFS_DIR_HEADER_SIZE;
993 } else {
994 *dentp = NULL;
995 dirs->size = 0;
996 return -SQFS_STOP_READDIR;
997 }
998
999 if (dirs->size > SQFS_EMPTY_FILE_SIZE) {
1000 /* Read follow-up (emitted) dir. header */
1001 memcpy(dirs->dir_header, dirs->table,
1002 SQFS_DIR_HEADER_SIZE);
1003 dirs->entry_count = dirs->dir_header->count + 1;
1004 ret = sqfs_read_entry(&dirs->entry, dirs->table +
1005 SQFS_DIR_HEADER_SIZE);
1006 if (ret)
1007 return -SQFS_STOP_READDIR;
1008
1009 dirs->table += SQFS_DIR_HEADER_SIZE;
1010 }
1011 } else {
1012 ret = sqfs_read_entry(&dirs->entry, dirs->table);
1013 if (ret)
1014 return -SQFS_STOP_READDIR;
1015 }
1016
1017 i_number = dirs->dir_header->inode_number + dirs->entry->inode_offset;
1018 ipos = sqfs_find_inode(dirs->inode_table, i_number, sblk->inodes,
1019 sblk->block_size);
1020
1021 base = (struct squashfs_base_inode *)ipos;
1022
1023 /* Set entry type and size */
1024 switch (dirs->entry->type) {
1025 case SQFS_DIR_TYPE:
1026 case SQFS_LDIR_TYPE:
1027 dent->type = FS_DT_DIR;
1028 break;
1029 case SQFS_REG_TYPE:
1030 case SQFS_LREG_TYPE:
1031 /*
1032 * Entries do not differentiate extended from regular types, so
1033 * it needs to be verified manually.
1034 */
1035 if (get_unaligned_le16(&base->inode_type) == SQFS_LREG_TYPE) {
1036 lreg = (struct squashfs_lreg_inode *)ipos;
1037 dent->size = get_unaligned_le64(&lreg->file_size);
1038 } else {
1039 reg = (struct squashfs_reg_inode *)ipos;
1040 dent->size = get_unaligned_le32(&reg->file_size);
1041 }
1042
1043 dent->type = FS_DT_REG;
1044 break;
1045 case SQFS_BLKDEV_TYPE:
1046 case SQFS_CHRDEV_TYPE:
1047 case SQFS_LBLKDEV_TYPE:
1048 case SQFS_LCHRDEV_TYPE:
1049 case SQFS_FIFO_TYPE:
1050 case SQFS_SOCKET_TYPE:
1051 case SQFS_LFIFO_TYPE:
1052 case SQFS_LSOCKET_TYPE:
1053 dent->type = SQFS_MISC_ENTRY_TYPE;
1054 break;
1055 case SQFS_SYMLINK_TYPE:
1056 case SQFS_LSYMLINK_TYPE:
1057 dent->type = FS_DT_LNK;
1058 break;
1059 default:
1060 return -SQFS_STOP_READDIR;
1061 }
1062
Miquel Raynala8e95fd2022-06-09 16:02:06 +02001063 /* Set entry name (capped at FS_DIRENT_NAME_LEN which is a U-Boot limitation) */
1064 name_size = min_t(u16, dirs->entry->name_size + 1, FS_DIRENT_NAME_LEN - 1);
1065 strncpy(dent->name, dirs->entry->name, name_size);
1066 dent->name[name_size] = '\0';
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001067
1068 offset = dirs->entry->name_size + 1 + SQFS_ENTRY_BASE_LENGTH;
1069 dirs->entry_count--;
1070
1071 /* Decrement size to be read */
1072 if (dirs->size > offset)
1073 dirs->size -= offset;
1074 else
1075 dirs->size = 0;
1076
1077 /* Keep a reference to the current entry before incrementing it */
1078 dirs->table += offset;
1079
1080 *dentp = dent;
1081
1082 return 0;
1083}
1084
1085int sqfs_probe(struct blk_desc *fs_dev_desc, struct disk_partition *fs_partition)
1086{
1087 struct squashfs_super_block *sblk;
1088 int ret;
1089
1090 ctxt.cur_dev = fs_dev_desc;
1091 ctxt.cur_part_info = *fs_partition;
1092
1093 ret = sqfs_read_sblk(&sblk);
1094 if (ret)
Richard Genoudcd72bc32020-11-03 12:11:21 +01001095 goto error;
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001096
1097 /* Make sure it has a valid SquashFS magic number*/
1098 if (get_unaligned_le32(&sblk->s_magic) != SQFS_MAGIC_NUMBER) {
Simon Glass9936fe32021-08-18 21:40:27 -06001099 debug("Bad magic number for SquashFS image.\n");
Richard Genoudac7c4db2020-11-03 12:11:19 +01001100 ret = -EINVAL;
1101 goto error;
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001102 }
1103
1104 ctxt.sblk = sblk;
1105
Joao Marcos Costa877576c2020-08-18 17:17:21 +02001106 ret = sqfs_decompressor_init(&ctxt);
Joao Marcos Costa877576c2020-08-18 17:17:21 +02001107 if (ret) {
Richard Genoudac7c4db2020-11-03 12:11:19 +01001108 goto error;
Joao Marcos Costa877576c2020-08-18 17:17:21 +02001109 }
1110
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001111 return 0;
Richard Genoudac7c4db2020-11-03 12:11:19 +01001112error:
1113 ctxt.cur_dev = NULL;
1114 free(ctxt.sblk);
1115 ctxt.sblk = NULL;
1116 return ret;
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001117}
1118
1119static char *sqfs_basename(char *path)
1120{
1121 char *fname;
1122
1123 fname = path + strlen(path) - 1;
1124 while (fname >= path) {
1125 if (*fname == '/') {
1126 fname++;
1127 break;
1128 }
1129
1130 fname--;
1131 }
1132
1133 return fname;
1134}
1135
1136static char *sqfs_dirname(char *path)
1137{
1138 char *fname;
1139
1140 fname = sqfs_basename(path);
1141 --fname;
1142 *fname = '\0';
1143
1144 return path;
1145}
1146
1147/*
1148 * Takes a path to file and splits it in two parts: the filename itself and the
1149 * directory's path, e.g.:
1150 * path: /path/to/file.txt
1151 * file: file.txt
1152 * dir: /path/to
1153 */
1154static int sqfs_split_path(char **file, char **dir, const char *path)
1155{
1156 char *dirc, *basec, *bname, *dname, *tmp_path;
1157 int ret = 0;
1158
Richard Genoud8cf2f022020-11-03 12:11:03 +01001159 *file = NULL;
1160 *dir = NULL;
1161 dirc = NULL;
1162 basec = NULL;
1163 bname = NULL;
1164 dname = NULL;
1165 tmp_path = NULL;
1166
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001167 /* check for first slash in path*/
1168 if (path[0] == '/') {
1169 tmp_path = strdup(path);
Richard Genoud8cf2f022020-11-03 12:11:03 +01001170 if (!tmp_path) {
1171 ret = -ENOMEM;
1172 goto out;
1173 }
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001174 } else {
1175 tmp_path = malloc(strlen(path) + 2);
Richard Genoud8cf2f022020-11-03 12:11:03 +01001176 if (!tmp_path) {
1177 ret = -ENOMEM;
1178 goto out;
1179 }
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001180 tmp_path[0] = '/';
1181 strcpy(tmp_path + 1, path);
1182 }
1183
1184 /* String duplicates */
1185 dirc = strdup(tmp_path);
1186 if (!dirc) {
1187 ret = -ENOMEM;
Richard Genoud8cf2f022020-11-03 12:11:03 +01001188 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001189 }
1190
1191 basec = strdup(tmp_path);
1192 if (!basec) {
1193 ret = -ENOMEM;
Richard Genoud8cf2f022020-11-03 12:11:03 +01001194 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001195 }
1196
1197 dname = sqfs_dirname(dirc);
1198 bname = sqfs_basename(basec);
1199
1200 *file = strdup(bname);
1201
1202 if (!*file) {
1203 ret = -ENOMEM;
Richard Genoud8cf2f022020-11-03 12:11:03 +01001204 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001205 }
1206
1207 if (*dname == '\0') {
1208 *dir = malloc(2);
1209 if (!*dir) {
1210 ret = -ENOMEM;
Richard Genoud8cf2f022020-11-03 12:11:03 +01001211 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001212 }
1213
1214 (*dir)[0] = '/';
1215 (*dir)[1] = '\0';
1216 } else {
1217 *dir = strdup(dname);
1218 if (!*dir) {
1219 ret = -ENOMEM;
Richard Genoud8cf2f022020-11-03 12:11:03 +01001220 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001221 }
1222 }
1223
Richard Genoud8cf2f022020-11-03 12:11:03 +01001224out:
1225 if (ret) {
1226 free(*file);
1227 free(*dir);
1228 *dir = NULL;
1229 *file = NULL;
1230 }
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001231 free(basec);
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001232 free(dirc);
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001233 free(tmp_path);
1234
1235 return ret;
1236}
1237
1238static int sqfs_get_regfile_info(struct squashfs_reg_inode *reg,
1239 struct squashfs_file_info *finfo,
1240 struct squashfs_fragment_block_entry *fentry,
1241 __le32 blksz)
1242{
1243 int datablk_count = 0, ret;
1244
1245 finfo->size = get_unaligned_le32(&reg->file_size);
1246 finfo->offset = get_unaligned_le32(&reg->offset);
1247 finfo->start = get_unaligned_le32(&reg->start_block);
1248 finfo->frag = SQFS_IS_FRAGMENTED(get_unaligned_le32(&reg->fragment));
1249
Joao Marcos Costaf1dfe4a2020-09-11 12:21:06 +02001250 if (finfo->frag && finfo->offset == 0xFFFFFFFF)
Joao Marcos Costab7fb3c32020-08-19 18:28:41 +02001251 return -EINVAL;
1252
Joao Marcos Costaf1dfe4a2020-09-11 12:21:06 +02001253 if (finfo->size < 1 || finfo->start == 0xFFFFFFFF)
1254 return -EINVAL;
1255
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001256 if (finfo->frag) {
1257 datablk_count = finfo->size / le32_to_cpu(blksz);
1258 ret = sqfs_frag_lookup(get_unaligned_le32(&reg->fragment),
1259 fentry);
1260 if (ret < 0)
1261 return -EINVAL;
Joao Marcos Costa8a724fa2021-05-17 18:20:38 -03001262 finfo->comp = ret;
Joao Marcos Costaf1dfe4a2020-09-11 12:21:06 +02001263 if (fentry->size < 1 || fentry->start == 0x7FFFFFFF)
Joao Marcos Costab7fb3c32020-08-19 18:28:41 +02001264 return -EINVAL;
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001265 } else {
1266 datablk_count = DIV_ROUND_UP(finfo->size, le32_to_cpu(blksz));
1267 }
1268
1269 finfo->blk_sizes = malloc(datablk_count * sizeof(u32));
1270 if (!finfo->blk_sizes)
1271 return -ENOMEM;
1272
1273 return datablk_count;
1274}
1275
1276static int sqfs_get_lregfile_info(struct squashfs_lreg_inode *lreg,
1277 struct squashfs_file_info *finfo,
1278 struct squashfs_fragment_block_entry *fentry,
1279 __le32 blksz)
1280{
1281 int datablk_count = 0, ret;
1282
1283 finfo->size = get_unaligned_le64(&lreg->file_size);
1284 finfo->offset = get_unaligned_le32(&lreg->offset);
1285 finfo->start = get_unaligned_le64(&lreg->start_block);
1286 finfo->frag = SQFS_IS_FRAGMENTED(get_unaligned_le32(&lreg->fragment));
1287
Joao Marcos Costaf1dfe4a2020-09-11 12:21:06 +02001288 if (finfo->frag && finfo->offset == 0xFFFFFFFF)
Joao Marcos Costab7fb3c32020-08-19 18:28:41 +02001289 return -EINVAL;
1290
Joao Marcos Costaf1dfe4a2020-09-11 12:21:06 +02001291 if (finfo->size < 1 || finfo->start == 0x7FFFFFFF)
1292 return -EINVAL;
1293
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001294 if (finfo->frag) {
1295 datablk_count = finfo->size / le32_to_cpu(blksz);
1296 ret = sqfs_frag_lookup(get_unaligned_le32(&lreg->fragment),
1297 fentry);
1298 if (ret < 0)
1299 return -EINVAL;
Joao Marcos Costa8a724fa2021-05-17 18:20:38 -03001300 finfo->comp = ret;
Joao Marcos Costaf1dfe4a2020-09-11 12:21:06 +02001301 if (fentry->size < 1 || fentry->start == 0x7FFFFFFF)
Joao Marcos Costab7fb3c32020-08-19 18:28:41 +02001302 return -EINVAL;
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001303 } else {
1304 datablk_count = DIV_ROUND_UP(finfo->size, le32_to_cpu(blksz));
1305 }
1306
1307 finfo->blk_sizes = malloc(datablk_count * sizeof(u32));
1308 if (!finfo->blk_sizes)
1309 return -ENOMEM;
1310
1311 return datablk_count;
1312}
1313
1314int sqfs_read(const char *filename, void *buf, loff_t offset, loff_t len,
1315 loff_t *actread)
1316{
Heinrich Schuchardt8e709432022-04-11 22:54:44 +02001317 char *dir = NULL, *fragment_block, *datablock = NULL;
Richard Genoud0caee472020-11-03 12:11:18 +01001318 char *fragment = NULL, *file = NULL, *resolved, *data;
Campbell Suter46446632020-11-23 15:40:03 +13001319 u64 start, n_blks, table_size, data_offset, table_offset, sparse_size;
Joao Marcos Costab87fd012020-08-18 17:17:22 +02001320 int ret, j, i_number, datablk_count = 0;
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001321 struct squashfs_super_block *sblk = ctxt.sblk;
1322 struct squashfs_fragment_block_entry frag_entry;
1323 struct squashfs_file_info finfo = {0};
1324 struct squashfs_symlink_inode *symlink;
1325 struct fs_dir_stream *dirsp = NULL;
1326 struct squashfs_dir_stream *dirs;
1327 struct squashfs_lreg_inode *lreg;
1328 struct squashfs_base_inode *base;
1329 struct squashfs_reg_inode *reg;
1330 unsigned long dest_len;
1331 struct fs_dirent *dent;
1332 unsigned char *ipos;
1333
1334 *actread = 0;
1335
Richard Genoudcfcb2242020-11-03 12:11:24 +01001336 if (offset) {
1337 /*
1338 * TODO: implement reading at an offset in file
1339 */
1340 printf("Error: reading at a specific offset in a squashfs file is not supported yet.\n");
1341 return -EINVAL;
1342 }
1343
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001344 /*
1345 * sqfs_opendir will uncompress inode and directory tables, and will
1346 * return a pointer to the directory that contains the requested file.
1347 */
1348 sqfs_split_path(&file, &dir, filename);
1349 ret = sqfs_opendir(dir, &dirsp);
1350 if (ret) {
Richard Genoud0caee472020-11-03 12:11:18 +01001351 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001352 }
1353
1354 dirs = (struct squashfs_dir_stream *)dirsp;
1355
1356 /* For now, only regular files are able to be loaded */
1357 while (!sqfs_readdir(dirsp, &dent)) {
1358 ret = strcmp(dent->name, file);
1359 if (!ret)
1360 break;
1361
1362 free(dirs->entry);
Richard Genoud0b8aa992020-11-03 12:11:11 +01001363 dirs->entry = NULL;
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001364 }
1365
1366 if (ret) {
1367 printf("File not found.\n");
1368 *actread = 0;
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001369 ret = -ENOENT;
Richard Genoud0caee472020-11-03 12:11:18 +01001370 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001371 }
1372
1373 i_number = dirs->dir_header->inode_number + dirs->entry->inode_offset;
1374 ipos = sqfs_find_inode(dirs->inode_table, i_number, sblk->inodes,
1375 sblk->block_size);
1376
1377 base = (struct squashfs_base_inode *)ipos;
1378 switch (get_unaligned_le16(&base->inode_type)) {
1379 case SQFS_REG_TYPE:
1380 reg = (struct squashfs_reg_inode *)ipos;
1381 datablk_count = sqfs_get_regfile_info(reg, &finfo, &frag_entry,
1382 sblk->block_size);
1383 if (datablk_count < 0) {
1384 ret = -EINVAL;
Richard Genoud0caee472020-11-03 12:11:18 +01001385 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001386 }
1387
1388 memcpy(finfo.blk_sizes, ipos + sizeof(*reg),
1389 datablk_count * sizeof(u32));
1390 break;
1391 case SQFS_LREG_TYPE:
1392 lreg = (struct squashfs_lreg_inode *)ipos;
1393 datablk_count = sqfs_get_lregfile_info(lreg, &finfo,
1394 &frag_entry,
1395 sblk->block_size);
1396 if (datablk_count < 0) {
1397 ret = -EINVAL;
Richard Genoud0caee472020-11-03 12:11:18 +01001398 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001399 }
1400
1401 memcpy(finfo.blk_sizes, ipos + sizeof(*lreg),
1402 datablk_count * sizeof(u32));
1403 break;
1404 case SQFS_SYMLINK_TYPE:
1405 case SQFS_LSYMLINK_TYPE:
1406 symlink = (struct squashfs_symlink_inode *)ipos;
1407 resolved = sqfs_resolve_symlink(symlink, filename);
1408 ret = sqfs_read(resolved, buf, offset, len, actread);
1409 free(resolved);
Richard Genoud0caee472020-11-03 12:11:18 +01001410 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001411 case SQFS_BLKDEV_TYPE:
1412 case SQFS_CHRDEV_TYPE:
1413 case SQFS_LBLKDEV_TYPE:
1414 case SQFS_LCHRDEV_TYPE:
1415 case SQFS_FIFO_TYPE:
1416 case SQFS_SOCKET_TYPE:
1417 case SQFS_LFIFO_TYPE:
1418 case SQFS_LSOCKET_TYPE:
1419 default:
1420 printf("Unsupported entry type\n");
1421 ret = -EINVAL;
Richard Genoud0caee472020-11-03 12:11:18 +01001422 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001423 }
1424
1425 /* If the user specifies a length, check its sanity */
1426 if (len) {
1427 if (len > finfo.size) {
1428 ret = -EINVAL;
Richard Genoud0caee472020-11-03 12:11:18 +01001429 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001430 }
1431
1432 finfo.size = len;
Richard Genoud6abdf012020-11-03 12:11:23 +01001433 } else {
1434 len = finfo.size;
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001435 }
1436
1437 if (datablk_count) {
1438 data_offset = finfo.start;
1439 datablock = malloc(get_unaligned_le32(&sblk->block_size));
1440 if (!datablock) {
1441 ret = -ENOMEM;
Richard Genoud0caee472020-11-03 12:11:18 +01001442 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001443 }
1444 }
1445
1446 for (j = 0; j < datablk_count; j++) {
Heinrich Schuchardt8e709432022-04-11 22:54:44 +02001447 char *data_buffer;
1448
Sean Nyekjaera8eb9622022-05-12 20:37:14 +02001449 start = lldiv(data_offset, ctxt.cur_dev->blksz);
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001450 table_size = SQFS_BLOCK_SIZE(finfo.blk_sizes[j]);
1451 table_offset = data_offset - (start * ctxt.cur_dev->blksz);
1452 n_blks = DIV_ROUND_UP(table_size + table_offset,
1453 ctxt.cur_dev->blksz);
1454
Campbell Suter46446632020-11-23 15:40:03 +13001455 /* Don't load any data for sparse blocks */
1456 if (finfo.blk_sizes[j] == 0) {
1457 n_blks = 0;
1458 table_offset = 0;
1459 data_buffer = NULL;
1460 data = NULL;
1461 } else {
1462 data_buffer = malloc_cache_aligned(n_blks * ctxt.cur_dev->blksz);
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001463
Campbell Suter46446632020-11-23 15:40:03 +13001464 if (!data_buffer) {
1465 ret = -ENOMEM;
1466 goto out;
1467 }
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001468
Campbell Suter46446632020-11-23 15:40:03 +13001469 ret = sqfs_disk_read(start, n_blks, data_buffer);
1470 if (ret < 0) {
1471 /*
1472 * Possible causes: too many data blocks or too large
1473 * SquashFS block size. Tip: re-compile the SquashFS
1474 * image with mksquashfs's -b <block_size> option.
1475 */
1476 printf("Error: too many data blocks to be read.\n");
1477 goto out;
1478 }
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001479
Campbell Suter46446632020-11-23 15:40:03 +13001480 data = data_buffer + table_offset;
1481 }
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001482
1483 /* Load the data */
Campbell Suter46446632020-11-23 15:40:03 +13001484 if (finfo.blk_sizes[j] == 0) {
1485 /* This is a sparse block */
1486 sparse_size = get_unaligned_le32(&sblk->block_size);
1487 if ((*actread + sparse_size) > len)
1488 sparse_size = len - *actread;
1489 memset(buf + *actread, 0, sparse_size);
1490 *actread += sparse_size;
1491 } else if (SQFS_COMPRESSED_BLOCK(finfo.blk_sizes[j])) {
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001492 dest_len = get_unaligned_le32(&sblk->block_size);
Joao Marcos Costab87fd012020-08-18 17:17:22 +02001493 ret = sqfs_decompress(&ctxt, datablock, &dest_len,
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001494 data, table_size);
1495 if (ret)
Richard Genoud0caee472020-11-03 12:11:18 +01001496 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001497
Richard Genoud6abdf012020-11-03 12:11:23 +01001498 if ((*actread + dest_len) > len)
1499 dest_len = len - *actread;
Richard Genoudcfcb2242020-11-03 12:11:24 +01001500 memcpy(buf + *actread, datablock, dest_len);
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001501 *actread += dest_len;
1502 } else {
Richard Genoud6abdf012020-11-03 12:11:23 +01001503 if ((*actread + table_size) > len)
1504 table_size = len - *actread;
Richard Genoudcfcb2242020-11-03 12:11:24 +01001505 memcpy(buf + *actread, data, table_size);
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001506 *actread += table_size;
1507 }
1508
1509 data_offset += table_size;
Heinrich Schuchardt8e709432022-04-11 22:54:44 +02001510 free(data_buffer);
Richard Genoud6abdf012020-11-03 12:11:23 +01001511 if (*actread >= len)
1512 break;
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001513 }
1514
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001515 /*
1516 * There is no need to continue if the file is not fragmented.
1517 */
1518 if (!finfo.frag) {
1519 ret = 0;
Richard Genoud0caee472020-11-03 12:11:18 +01001520 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001521 }
1522
Sean Nyekjaera8eb9622022-05-12 20:37:14 +02001523 start = lldiv(frag_entry.start, ctxt.cur_dev->blksz);
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001524 table_size = SQFS_BLOCK_SIZE(frag_entry.size);
1525 table_offset = frag_entry.start - (start * ctxt.cur_dev->blksz);
1526 n_blks = DIV_ROUND_UP(table_size + table_offset, ctxt.cur_dev->blksz);
1527
1528 fragment = malloc_cache_aligned(n_blks * ctxt.cur_dev->blksz);
1529
1530 if (!fragment) {
1531 ret = -ENOMEM;
Richard Genoud0caee472020-11-03 12:11:18 +01001532 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001533 }
1534
1535 ret = sqfs_disk_read(start, n_blks, fragment);
1536 if (ret < 0)
Richard Genoud0caee472020-11-03 12:11:18 +01001537 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001538
1539 /* File compressed and fragmented */
1540 if (finfo.frag && finfo.comp) {
1541 dest_len = get_unaligned_le32(&sblk->block_size);
1542 fragment_block = malloc(dest_len);
1543 if (!fragment_block) {
1544 ret = -ENOMEM;
Richard Genoud0caee472020-11-03 12:11:18 +01001545 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001546 }
1547
Joao Marcos Costab87fd012020-08-18 17:17:22 +02001548 ret = sqfs_decompress(&ctxt, fragment_block, &dest_len,
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001549 (void *)fragment + table_offset,
1550 frag_entry.size);
1551 if (ret) {
1552 free(fragment_block);
Richard Genoud0caee472020-11-03 12:11:18 +01001553 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001554 }
1555
Joao Marcos Costa8a724fa2021-05-17 18:20:38 -03001556 memcpy(buf + *actread, &fragment_block[finfo.offset], finfo.size - *actread);
1557 *actread = finfo.size;
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001558
1559 free(fragment_block);
1560
1561 } else if (finfo.frag && !finfo.comp) {
1562 fragment_block = (void *)fragment + table_offset;
1563
Joao Marcos Costa8a724fa2021-05-17 18:20:38 -03001564 memcpy(buf + *actread, &fragment_block[finfo.offset], finfo.size - *actread);
1565 *actread = finfo.size;
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001566 }
1567
Richard Genoud0caee472020-11-03 12:11:18 +01001568out:
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001569 free(fragment);
Heinrich Schuchardt8e709432022-04-11 22:54:44 +02001570 free(datablock);
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001571 free(file);
1572 free(dir);
Richard Genoud0caee472020-11-03 12:11:18 +01001573 free(finfo.blk_sizes);
Richard Genoud84a5f572020-11-03 12:11:13 +01001574 sqfs_closedir(dirsp);
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001575
1576 return ret;
1577}
1578
1579int sqfs_size(const char *filename, loff_t *size)
1580{
1581 struct squashfs_super_block *sblk = ctxt.sblk;
1582 struct squashfs_symlink_inode *symlink;
1583 struct fs_dir_stream *dirsp = NULL;
1584 struct squashfs_base_inode *base;
1585 struct squashfs_dir_stream *dirs;
1586 struct squashfs_lreg_inode *lreg;
1587 struct squashfs_reg_inode *reg;
1588 char *dir, *file, *resolved;
1589 struct fs_dirent *dent;
1590 unsigned char *ipos;
1591 int ret, i_number;
1592
1593 sqfs_split_path(&file, &dir, filename);
1594 /*
1595 * sqfs_opendir will uncompress inode and directory tables, and will
1596 * return a pointer to the directory that contains the requested file.
1597 */
1598 ret = sqfs_opendir(dir, &dirsp);
1599 if (ret) {
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001600 ret = -EINVAL;
1601 goto free_strings;
1602 }
1603
1604 dirs = (struct squashfs_dir_stream *)dirsp;
1605
1606 while (!sqfs_readdir(dirsp, &dent)) {
1607 ret = strcmp(dent->name, file);
1608 if (!ret)
1609 break;
1610 free(dirs->entry);
Richard Genoudc3dadcb2020-11-03 12:11:09 +01001611 dirs->entry = NULL;
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001612 }
1613
1614 if (ret) {
1615 printf("File not found.\n");
1616 *size = 0;
1617 ret = -EINVAL;
1618 goto free_strings;
1619 }
1620
1621 i_number = dirs->dir_header->inode_number + dirs->entry->inode_offset;
1622 ipos = sqfs_find_inode(dirs->inode_table, i_number, sblk->inodes,
1623 sblk->block_size);
1624 free(dirs->entry);
Richard Genoudc3dadcb2020-11-03 12:11:09 +01001625 dirs->entry = NULL;
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001626
1627 base = (struct squashfs_base_inode *)ipos;
1628 switch (get_unaligned_le16(&base->inode_type)) {
1629 case SQFS_REG_TYPE:
1630 reg = (struct squashfs_reg_inode *)ipos;
1631 *size = get_unaligned_le32(&reg->file_size);
1632 break;
1633 case SQFS_LREG_TYPE:
1634 lreg = (struct squashfs_lreg_inode *)ipos;
1635 *size = get_unaligned_le64(&lreg->file_size);
1636 break;
1637 case SQFS_SYMLINK_TYPE:
1638 case SQFS_LSYMLINK_TYPE:
1639 symlink = (struct squashfs_symlink_inode *)ipos;
1640 resolved = sqfs_resolve_symlink(symlink, filename);
1641 ret = sqfs_size(resolved, size);
1642 free(resolved);
1643 break;
1644 case SQFS_BLKDEV_TYPE:
1645 case SQFS_CHRDEV_TYPE:
1646 case SQFS_LBLKDEV_TYPE:
1647 case SQFS_LCHRDEV_TYPE:
1648 case SQFS_FIFO_TYPE:
1649 case SQFS_SOCKET_TYPE:
1650 case SQFS_LFIFO_TYPE:
1651 case SQFS_LSOCKET_TYPE:
1652 default:
1653 printf("Unable to recover entry's size.\n");
1654 *size = 0;
1655 ret = -EINVAL;
1656 break;
1657 }
1658
1659free_strings:
1660 free(dir);
1661 free(file);
1662
1663 sqfs_closedir(dirsp);
1664
1665 return ret;
1666}
1667
Richard Genoudcfca20c2020-11-03 12:11:26 +01001668int sqfs_exists(const char *filename)
1669{
1670 struct fs_dir_stream *dirsp = NULL;
1671 struct squashfs_dir_stream *dirs;
1672 char *dir, *file;
1673 struct fs_dirent *dent;
1674 int ret;
1675
1676 sqfs_split_path(&file, &dir, filename);
1677 /*
1678 * sqfs_opendir will uncompress inode and directory tables, and will
1679 * return a pointer to the directory that contains the requested file.
1680 */
1681 ret = sqfs_opendir(dir, &dirsp);
1682 if (ret) {
1683 ret = -EINVAL;
1684 goto free_strings;
1685 }
1686
1687 dirs = (struct squashfs_dir_stream *)dirsp;
1688
1689 while (!sqfs_readdir(dirsp, &dent)) {
1690 ret = strcmp(dent->name, file);
1691 if (!ret)
1692 break;
1693 free(dirs->entry);
1694 dirs->entry = NULL;
1695 }
1696
1697 sqfs_closedir(dirsp);
1698
1699free_strings:
1700 free(dir);
1701 free(file);
1702
1703 return ret == 0;
1704}
1705
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001706void sqfs_close(void)
1707{
Richard Genoud1d95a1e2020-11-24 18:07:52 +01001708 sqfs_decompressor_cleanup(&ctxt);
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001709 free(ctxt.sblk);
Richard Genoud1d95a1e2020-11-24 18:07:52 +01001710 ctxt.sblk = NULL;
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001711 ctxt.cur_dev = NULL;
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001712}
1713
1714void sqfs_closedir(struct fs_dir_stream *dirs)
1715{
1716 struct squashfs_dir_stream *sqfs_dirs;
1717
Heinrich Schuchardt836bba42021-02-01 03:28:48 +01001718 if (!dirs)
1719 return;
1720
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001721 sqfs_dirs = (struct squashfs_dir_stream *)dirs;
1722 free(sqfs_dirs->inode_table);
1723 free(sqfs_dirs->dir_table);
1724 free(sqfs_dirs->dir_header);
Richard Genoud2575eb22020-11-03 12:11:02 +01001725 free(sqfs_dirs);
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001726}