blob: 16a07c0622bdc7140370013195974811324cbcbd [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);
Heinrich Schuchardt8cace452022-05-10 21:53:25 +020053 *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);
Kasper Revsbech42d59662022-12-01 16:30:32 +010090 start_ = lldiv(le64_to_cpu(start), ctxt.cur_dev->blksz);
Joao Marcos Costa29da3742020-07-30 15:33:47 +020091 *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{
David Oberhollenzerddf1dfb2022-12-25 11:05:24 +0100103 u64 start, end, exp_tbl, n_blks, src_len, table_offset, start_block;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200104 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
David Oberhollenzerddf1dfb2022-12-25 11:05:24 +0100118 start = get_unaligned_le64(&sblk->fragment_table_start);
119 end = get_unaligned_le64(&sblk->id_table_start);
120 exp_tbl = get_unaligned_le64(&sblk->export_table_start);
121
122 if (exp_tbl > start && exp_tbl < end)
123 end = exp_tbl;
124
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200125 n_blks = sqfs_calc_n_blks(sblk->fragment_table_start,
David Oberhollenzerddf1dfb2022-12-25 11:05:24 +0100126 cpu_to_le64(end), &table_offset);
127
128 start /= ctxt.cur_dev->blksz;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200129
130 /* Allocate a proper sized buffer to store the fragment index table */
131 table = malloc_cache_aligned(n_blks * ctxt.cur_dev->blksz);
Richard Genoud07726e12020-11-03 12:11:15 +0100132 if (!table) {
133 ret = -ENOMEM;
134 goto out;
135 }
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200136
137 if (sqfs_disk_read(start, n_blks, table) < 0) {
Richard Genoud07726e12020-11-03 12:11:15 +0100138 ret = -EINVAL;
139 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200140 }
141
142 block = SQFS_FRAGMENT_INDEX(inode_fragment_index);
143 offset = SQFS_FRAGMENT_INDEX_OFFSET(inode_fragment_index);
144
145 /*
146 * Get the start offset of the metadata block that contains the right
147 * fragment block entry
148 */
149 start_block = get_unaligned_le64(table + table_offset + block *
150 sizeof(u64));
151
152 start = start_block / ctxt.cur_dev->blksz;
153 n_blks = sqfs_calc_n_blks(cpu_to_le64(start_block),
154 sblk->fragment_table_start, &table_offset);
155
156 metadata_buffer = malloc_cache_aligned(n_blks * ctxt.cur_dev->blksz);
157 if (!metadata_buffer) {
158 ret = -ENOMEM;
Richard Genoud07726e12020-11-03 12:11:15 +0100159 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200160 }
161
162 if (sqfs_disk_read(start, n_blks, metadata_buffer) < 0) {
163 ret = -EINVAL;
Richard Genoud07726e12020-11-03 12:11:15 +0100164 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200165 }
166
167 /* Every metadata block starts with a 16-bit header */
168 header = get_unaligned_le16(metadata_buffer + table_offset);
169 metadata = metadata_buffer + table_offset + SQFS_HEADER_SIZE;
170
Joao Marcos Costaf1dfe4a2020-09-11 12:21:06 +0200171 if (!metadata || !header) {
Joao Marcos Costab7fb3c32020-08-19 18:28:41 +0200172 ret = -ENOMEM;
Richard Genoud07726e12020-11-03 12:11:15 +0100173 goto out;
Joao Marcos Costab7fb3c32020-08-19 18:28:41 +0200174 }
175
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200176 entries = malloc(SQFS_METADATA_BLOCK_SIZE);
177 if (!entries) {
178 ret = -ENOMEM;
Richard Genoud07726e12020-11-03 12:11:15 +0100179 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200180 }
181
182 if (SQFS_COMPRESSED_METADATA(header)) {
183 src_len = SQFS_METADATA_SIZE(header);
184 dest_len = SQFS_METADATA_BLOCK_SIZE;
Joao Marcos Costab87fd012020-08-18 17:17:22 +0200185 ret = sqfs_decompress(&ctxt, entries, &dest_len, metadata,
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200186 src_len);
187 if (ret) {
188 ret = -EINVAL;
Richard Genoud07726e12020-11-03 12:11:15 +0100189 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200190 }
191 } else {
192 memcpy(entries, metadata, SQFS_METADATA_SIZE(header));
193 }
194
195 *e = entries[offset];
196 ret = SQFS_COMPRESSED_BLOCK(e->size);
197
Richard Genoud07726e12020-11-03 12:11:15 +0100198out:
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200199 free(entries);
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200200 free(metadata_buffer);
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200201 free(table);
202
203 return ret;
204}
205
206/*
207 * The entry name is a flexible array member, and we don't know its size before
208 * actually reading the entry. So we need a first copy to retrieve this size so
209 * we can finally copy the whole struct.
210 */
211static int sqfs_read_entry(struct squashfs_directory_entry **dest, void *src)
212{
213 struct squashfs_directory_entry *tmp;
214 u16 sz;
215
216 tmp = src;
217 sz = get_unaligned_le16(src + sizeof(*tmp) - sizeof(u16));
218 /*
219 * 'src' points to the begin of a directory entry, and 'sz' gets its
220 * 'name_size' member's value. name_size is actually the string
221 * length - 1, so adding 2 compensates this difference and adds space
222 * for the trailling null byte.
223 */
224 *dest = malloc(sizeof(*tmp) + sz + 2);
225 if (!*dest)
226 return -ENOMEM;
227
228 memcpy(*dest, src, sizeof(*tmp) + sz + 1);
229 (*dest)->name[sz + 1] = '\0';
230
231 return 0;
232}
233
234static int sqfs_get_tokens_length(char **tokens, int count)
235{
236 int length = 0, i;
237
238 /*
239 * 1 is added to the result of strlen to consider the slash separator
240 * between the tokens.
241 */
242 for (i = 0; i < count; i++)
243 length += strlen(tokens[i]) + 1;
244
245 return length;
246}
247
248/* Takes a token list and returns a single string with '/' as separator. */
249static char *sqfs_concat_tokens(char **token_list, int token_count)
250{
251 char *result;
252 int i, length = 0, offset = 0;
253
254 length = sqfs_get_tokens_length(token_list, token_count);
255
256 result = malloc(length + 1);
Richard Genoud489e7ae2020-11-03 12:11:08 +0100257 if (!result)
258 return NULL;
259
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200260 result[length] = '\0';
261
262 for (i = 0; i < token_count; i++) {
263 strcpy(result + offset, token_list[i]);
264 offset += strlen(token_list[i]);
265 result[offset++] = '/';
266 }
267
268 return result;
269}
270
271/*
272 * Differently from sqfs_concat_tokens, sqfs_join writes the result into a
273 * previously allocated string, and returns the number of bytes written.
274 */
275static int sqfs_join(char **strings, char *dest, int start, int end,
276 char separator)
277{
278 int i, offset = 0;
279
280 for (i = start; i < end; i++) {
281 strcpy(dest + offset, strings[i]);
282 offset += strlen(strings[i]);
283 if (i < end - 1)
284 dest[offset++] = separator;
285 }
286
287 return offset;
288}
289
290/*
291 * Fills the given token list using its size (count) and a source string (str)
292 */
293static int sqfs_tokenize(char **tokens, int count, const char *str)
294{
Joao Marcos Costab7fb3c32020-08-19 18:28:41 +0200295 int i, j, ret = 0;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200296 char *aux, *strc;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200297
298 strc = strdup(str);
299 if (!strc)
300 return -ENOMEM;
301
302 if (!strcmp(strc, "/")) {
303 tokens[0] = strdup(strc);
304 if (!tokens[0]) {
Joao Marcos Costab7fb3c32020-08-19 18:28:41 +0200305 ret = -ENOMEM;
306 goto free_strc;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200307 }
308 } else {
309 for (j = 0; j < count; j++) {
310 aux = strtok(!j ? strc : NULL, "/");
311 tokens[j] = strdup(aux);
312 if (!tokens[j]) {
313 for (i = 0; i < j; i++)
314 free(tokens[i]);
Joao Marcos Costab7fb3c32020-08-19 18:28:41 +0200315 ret = -ENOMEM;
316 goto free_strc;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200317 }
318 }
319 }
320
Joao Marcos Costab7fb3c32020-08-19 18:28:41 +0200321free_strc:
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200322 free(strc);
323
Joao Marcos Costab7fb3c32020-08-19 18:28:41 +0200324 return ret;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200325}
326
327/*
328 * Remove last 'updir + 1' tokens from the base path tokens list. This leaves us
329 * with a token list containing only the tokens needed to form the resolved
330 * path, and returns the decremented size of the token list.
331 */
332static int sqfs_clean_base_path(char **base, int count, int updir)
333{
334 int i;
335
336 for (i = count - updir - 1; i < count; i++)
337 free(base[i]);
338
339 return count - updir - 1;
340}
341
342/*
343 * Given the base ("current dir.") path and the relative one, generate the
344 * absolute path.
345 */
346static char *sqfs_get_abs_path(const char *base, const char *rel)
347{
348 char **base_tokens, **rel_tokens, *resolved = NULL;
349 int ret, bc, rc, i, updir = 0, resolved_size = 0, offset = 0;
350
Richard Genoudcf9072a2020-11-03 12:11:17 +0100351 base_tokens = NULL;
352 rel_tokens = NULL;
353
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200354 /* Memory allocation for the token lists */
355 bc = sqfs_count_tokens(base);
356 rc = sqfs_count_tokens(rel);
357 if (bc < 1 || rc < 1)
358 return NULL;
359
Richard Genoudcf9072a2020-11-03 12:11:17 +0100360 base_tokens = calloc(bc, sizeof(char *));
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200361 if (!base_tokens)
362 return NULL;
363
Richard Genoudcf9072a2020-11-03 12:11:17 +0100364 rel_tokens = calloc(rc, sizeof(char *));
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200365 if (!rel_tokens)
Richard Genoudcf9072a2020-11-03 12:11:17 +0100366 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200367
368 /* Fill token lists */
369 ret = sqfs_tokenize(base_tokens, bc, base);
370 if (ret)
Richard Genoudcf9072a2020-11-03 12:11:17 +0100371 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200372
Richard Genoud9cb30362020-11-03 12:11:16 +0100373 ret = sqfs_tokenize(rel_tokens, rc, rel);
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200374 if (ret)
Richard Genoudcf9072a2020-11-03 12:11:17 +0100375 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200376
377 /* count '..' occurrences in target path */
378 for (i = 0; i < rc; i++) {
379 if (!strcmp(rel_tokens[i], ".."))
380 updir++;
381 }
382
383 /* Remove the last token and the '..' occurrences */
384 bc = sqfs_clean_base_path(base_tokens, bc, updir);
385 if (bc < 0)
Richard Genoudcf9072a2020-11-03 12:11:17 +0100386 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200387
388 /* Calculate resolved path size */
389 if (!bc)
390 resolved_size++;
391
392 resolved_size += sqfs_get_tokens_length(base_tokens, bc) +
393 sqfs_get_tokens_length(rel_tokens, rc);
394
395 resolved = malloc(resolved_size + 1);
396 if (!resolved)
Richard Genoudcf9072a2020-11-03 12:11:17 +0100397 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200398
399 /* Set resolved path */
400 memset(resolved, '\0', resolved_size + 1);
401 offset += sqfs_join(base_tokens, resolved + offset, 0, bc, '/');
402 resolved[offset++] = '/';
403 offset += sqfs_join(rel_tokens, resolved + offset, updir, rc, '/');
404
Richard Genoudcf9072a2020-11-03 12:11:17 +0100405out:
406 if (rel_tokens)
407 for (i = 0; i < rc; i++)
408 free(rel_tokens[i]);
409 if (base_tokens)
410 for (i = 0; i < bc; i++)
411 free(base_tokens[i]);
412
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200413 free(rel_tokens);
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200414 free(base_tokens);
415
416 return resolved;
417}
418
419static char *sqfs_resolve_symlink(struct squashfs_symlink_inode *sym,
420 const char *base_path)
421{
422 char *resolved, *target;
423 u32 sz;
424
Richard Weinberger18ef5f32024-08-02 18:36:44 +0200425 if (__builtin_add_overflow(get_unaligned_le32(&sym->symlink_size), 1, &sz))
426 return NULL;
427
428 target = malloc(sz);
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200429 if (!target)
430 return NULL;
431
432 /*
433 * There is no trailling null byte in the symlink's target path, so a
434 * copy is made and a '\0' is added at its end.
435 */
Richard Weinberger18ef5f32024-08-02 18:36:44 +0200436 target[sz - 1] = '\0';
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200437 /* Get target name (relative path) */
Richard Weinberger18ef5f32024-08-02 18:36:44 +0200438 strncpy(target, sym->symlink, sz - 1);
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200439
440 /* Relative -> absolute path conversion */
441 resolved = sqfs_get_abs_path(base_path, target);
442
443 free(target);
444
445 return resolved;
446}
447
448/*
449 * m_list contains each metadata block's position, and m_count is the number of
450 * elements of m_list. Those metadata blocks come from the compressed directory
451 * table.
452 */
453static int sqfs_search_dir(struct squashfs_dir_stream *dirs, char **token_list,
454 int token_count, u32 *m_list, int m_count)
455{
456 struct squashfs_super_block *sblk = ctxt.sblk;
457 char *path, *target, **sym_tokens, *res, *rem;
Richard Genoud2762f652020-11-03 12:11:06 +0100458 int j, ret = 0, new_inode_number, offset;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200459 struct squashfs_symlink_inode *sym;
Joao Marcos Costaf1dfe4a2020-09-11 12:21:06 +0200460 struct squashfs_ldir_inode *ldir;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200461 struct squashfs_dir_inode *dir;
462 struct fs_dir_stream *dirsp;
463 struct fs_dirent *dent;
464 unsigned char *table;
465
Richard Genoud2762f652020-11-03 12:11:06 +0100466 res = NULL;
467 rem = NULL;
468 path = NULL;
469 target = NULL;
470 sym_tokens = NULL;
471
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200472 dirsp = (struct fs_dir_stream *)dirs;
473
474 /* Start by root inode */
475 table = sqfs_find_inode(dirs->inode_table, le32_to_cpu(sblk->inodes),
476 sblk->inodes, sblk->block_size);
477
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200478 dir = (struct squashfs_dir_inode *)table;
Joao Marcos Costaf1dfe4a2020-09-11 12:21:06 +0200479 ldir = (struct squashfs_ldir_inode *)table;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200480
481 /* get directory offset in directory table */
482 offset = sqfs_dir_offset(table, m_list, m_count);
483 dirs->table = &dirs->dir_table[offset];
484
485 /* Setup directory header */
486 dirs->dir_header = malloc(SQFS_DIR_HEADER_SIZE);
487 if (!dirs->dir_header)
488 return -ENOMEM;
489
490 memcpy(dirs->dir_header, dirs->table, SQFS_DIR_HEADER_SIZE);
491
492 /* Initialize squashfs_dir_stream members */
493 dirs->table += SQFS_DIR_HEADER_SIZE;
494 dirs->size = get_unaligned_le16(&dir->file_size) - SQFS_DIR_HEADER_SIZE;
495 dirs->entry_count = dirs->dir_header->count + 1;
496
497 /* No path given -> root directory */
498 if (!strcmp(token_list[0], "/")) {
499 dirs->table = &dirs->dir_table[offset];
500 memcpy(&dirs->i_dir, dir, sizeof(*dir));
501 return 0;
502 }
503
504 for (j = 0; j < token_count; j++) {
505 if (!sqfs_is_dir(get_unaligned_le16(&dir->inode_type))) {
506 printf("** Cannot find directory. **\n");
Richard Genoud2762f652020-11-03 12:11:06 +0100507 ret = -EINVAL;
508 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200509 }
510
511 while (!sqfs_readdir(dirsp, &dent)) {
512 ret = strcmp(dent->name, token_list[j]);
513 if (!ret)
514 break;
515 free(dirs->entry);
Richard Genoude405fc42020-11-03 12:11:05 +0100516 dirs->entry = NULL;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200517 }
518
519 if (ret) {
520 printf("** Cannot find directory. **\n");
Richard Genoud2762f652020-11-03 12:11:06 +0100521 ret = -EINVAL;
522 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200523 }
524
525 /* Redefine inode as the found token */
526 new_inode_number = dirs->entry->inode_offset +
527 dirs->dir_header->inode_number;
528
529 /* Get reference to inode in the inode table */
530 table = sqfs_find_inode(dirs->inode_table, new_inode_number,
531 sblk->inodes, sblk->block_size);
532 dir = (struct squashfs_dir_inode *)table;
533
534 /* Check for symbolic link and inode type sanity */
535 if (get_unaligned_le16(&dir->inode_type) == SQFS_SYMLINK_TYPE) {
536 sym = (struct squashfs_symlink_inode *)table;
537 /* Get first j + 1 tokens */
538 path = sqfs_concat_tokens(token_list, j + 1);
Richard Genoud2762f652020-11-03 12:11:06 +0100539 if (!path) {
540 ret = -ENOMEM;
541 goto out;
542 }
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200543 /* Resolve for these tokens */
544 target = sqfs_resolve_symlink(sym, path);
Richard Genoud2762f652020-11-03 12:11:06 +0100545 if (!target) {
546 ret = -ENOMEM;
547 goto out;
548 }
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200549 /* Join remaining tokens */
550 rem = sqfs_concat_tokens(token_list + j + 1, token_count -
551 j - 1);
Richard Genoud2762f652020-11-03 12:11:06 +0100552 if (!rem) {
553 ret = -ENOMEM;
554 goto out;
555 }
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200556 /* Concatenate remaining tokens and symlink's target */
557 res = malloc(strlen(rem) + strlen(target) + 1);
Richard Genoud2762f652020-11-03 12:11:06 +0100558 if (!res) {
559 ret = -ENOMEM;
560 goto out;
561 }
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200562 strcpy(res, target);
563 res[strlen(target)] = '/';
564 strcpy(res + strlen(target) + 1, rem);
565 token_count = sqfs_count_tokens(res);
566
Richard Genoud2762f652020-11-03 12:11:06 +0100567 if (token_count < 0) {
568 ret = -EINVAL;
569 goto out;
570 }
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200571
572 sym_tokens = malloc(token_count * sizeof(char *));
Richard Genoud2762f652020-11-03 12:11:06 +0100573 if (!sym_tokens) {
574 ret = -EINVAL;
575 goto out;
576 }
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200577
578 /* Fill tokens list */
579 ret = sqfs_tokenize(sym_tokens, token_count, res);
Richard Genoud2762f652020-11-03 12:11:06 +0100580 if (ret) {
581 ret = -EINVAL;
582 goto out;
583 }
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200584 free(dirs->entry);
Richard Genoude405fc42020-11-03 12:11:05 +0100585 dirs->entry = NULL;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200586
587 ret = sqfs_search_dir(dirs, sym_tokens, token_count,
588 m_list, m_count);
Richard Genoud2762f652020-11-03 12:11:06 +0100589 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200590 } else if (!sqfs_is_dir(get_unaligned_le16(&dir->inode_type))) {
591 printf("** Cannot find directory. **\n");
592 free(dirs->entry);
Richard Genoude405fc42020-11-03 12:11:05 +0100593 dirs->entry = NULL;
Richard Genoud2762f652020-11-03 12:11:06 +0100594 ret = -EINVAL;
595 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200596 }
597
598 /* Check if it is an extended dir. */
599 if (get_unaligned_le16(&dir->inode_type) == SQFS_LDIR_TYPE)
600 ldir = (struct squashfs_ldir_inode *)table;
601
602 /* Get dir. offset into the directory table */
603 offset = sqfs_dir_offset(table, m_list, m_count);
604 dirs->table = &dirs->dir_table[offset];
605
606 /* Copy directory header */
607 memcpy(dirs->dir_header, &dirs->dir_table[offset],
608 SQFS_DIR_HEADER_SIZE);
609
610 /* Check for empty directory */
611 if (sqfs_is_empty_dir(table)) {
612 printf("Empty directory.\n");
613 free(dirs->entry);
Richard Genoude405fc42020-11-03 12:11:05 +0100614 dirs->entry = NULL;
Richard Genoud2762f652020-11-03 12:11:06 +0100615 ret = SQFS_EMPTY_DIR;
616 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200617 }
618
619 dirs->table += SQFS_DIR_HEADER_SIZE;
620 dirs->size = get_unaligned_le16(&dir->file_size);
621 dirs->entry_count = dirs->dir_header->count + 1;
622 dirs->size -= SQFS_DIR_HEADER_SIZE;
623 free(dirs->entry);
Richard Genoude405fc42020-11-03 12:11:05 +0100624 dirs->entry = NULL;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200625 }
626
627 offset = sqfs_dir_offset(table, m_list, m_count);
628 dirs->table = &dirs->dir_table[offset];
629
630 if (get_unaligned_le16(&dir->inode_type) == SQFS_DIR_TYPE)
631 memcpy(&dirs->i_dir, dir, sizeof(*dir));
632 else
633 memcpy(&dirs->i_ldir, ldir, sizeof(*ldir));
634
Richard Genoud2762f652020-11-03 12:11:06 +0100635out:
636 free(res);
637 free(rem);
638 free(path);
639 free(target);
640 free(sym_tokens);
641 return ret;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200642}
643
644/*
645 * Inode and directory tables are stored as a series of metadata blocks, and
646 * given the compressed size of this table, we can calculate how much metadata
647 * blocks are needed to store the result of the decompression, since a
648 * decompressed metadata block should have a size of 8KiB.
649 */
650static int sqfs_count_metablks(void *table, u32 offset, int table_size)
651{
652 int count = 0, cur_size = 0, ret;
653 u32 data_size;
654 bool comp;
655
656 do {
657 ret = sqfs_read_metablock(table, offset + cur_size, &comp,
658 &data_size);
659 if (ret)
660 return -EINVAL;
661 cur_size += data_size + SQFS_HEADER_SIZE;
662 count++;
663 } while (cur_size < table_size);
664
665 return count;
666}
667
668/*
669 * Storing the metadata blocks header's positions will be useful while looking
670 * for an entry in the directory table, using the reference (index and offset)
671 * given by its inode.
672 */
673static int sqfs_get_metablk_pos(u32 *pos_list, void *table, u32 offset,
674 int metablks_count)
675{
676 u32 data_size, cur_size = 0;
677 int j, ret = 0;
678 bool comp;
679
680 if (!metablks_count)
681 return -EINVAL;
682
683 for (j = 0; j < metablks_count; j++) {
684 ret = sqfs_read_metablock(table, offset + cur_size, &comp,
685 &data_size);
686 if (ret)
687 return -EINVAL;
688
689 cur_size += data_size + SQFS_HEADER_SIZE;
690 pos_list[j] = cur_size;
691 }
692
693 return ret;
694}
695
696static int sqfs_read_inode_table(unsigned char **inode_table)
697{
698 struct squashfs_super_block *sblk = ctxt.sblk;
699 u64 start, n_blks, table_offset, table_size;
Joao Marcos Costab87fd012020-08-18 17:17:22 +0200700 int j, ret = 0, metablks_count;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200701 unsigned char *src_table, *itb;
702 u32 src_len, dest_offset = 0;
Joao Marcos Costab7fb3c32020-08-19 18:28:41 +0200703 unsigned long dest_len = 0;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200704 bool compressed;
705
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200706 table_size = get_unaligned_le64(&sblk->directory_table_start) -
707 get_unaligned_le64(&sblk->inode_table_start);
708 start = get_unaligned_le64(&sblk->inode_table_start) /
709 ctxt.cur_dev->blksz;
710 n_blks = sqfs_calc_n_blks(sblk->inode_table_start,
711 sblk->directory_table_start, &table_offset);
712
713 /* Allocate a proper sized buffer (itb) to store the inode table */
714 itb = malloc_cache_aligned(n_blks * ctxt.cur_dev->blksz);
715 if (!itb)
716 return -ENOMEM;
717
718 if (sqfs_disk_read(start, n_blks, itb) < 0) {
719 ret = -EINVAL;
720 goto free_itb;
721 }
722
723 /* Parse inode table (metadata block) header */
724 ret = sqfs_read_metablock(itb, table_offset, &compressed, &src_len);
725 if (ret) {
726 ret = -EINVAL;
727 goto free_itb;
728 }
729
730 /* Calculate size to store the whole decompressed table */
731 metablks_count = sqfs_count_metablks(itb, table_offset, table_size);
732 if (metablks_count < 1) {
733 ret = -EINVAL;
734 goto free_itb;
735 }
736
Miquel Raynaledaf6042022-06-27 12:20:03 +0200737 *inode_table = kcalloc(metablks_count, SQFS_METADATA_BLOCK_SIZE,
738 GFP_KERNEL);
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200739 if (!*inode_table) {
740 ret = -ENOMEM;
Lars Weber205783f2022-01-13 14:28:45 +0100741 printf("Error: failed to allocate squashfs inode_table of size %i, increasing CONFIG_SYS_MALLOC_LEN could help\n",
742 metablks_count * SQFS_METADATA_BLOCK_SIZE);
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200743 goto free_itb;
744 }
745
746 src_table = itb + table_offset + SQFS_HEADER_SIZE;
747
748 /* Extract compressed Inode table */
749 for (j = 0; j < metablks_count; j++) {
750 sqfs_read_metablock(itb, table_offset, &compressed, &src_len);
751 if (compressed) {
752 dest_len = SQFS_METADATA_BLOCK_SIZE;
Joao Marcos Costab87fd012020-08-18 17:17:22 +0200753 ret = sqfs_decompress(&ctxt, *inode_table +
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200754 dest_offset, &dest_len,
755 src_table, src_len);
756 if (ret) {
757 free(*inode_table);
Richard Genouda62528d2020-11-03 12:11:07 +0100758 *inode_table = NULL;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200759 goto free_itb;
760 }
761
Joao Marcos Costab7fb3c32020-08-19 18:28:41 +0200762 dest_offset += dest_len;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200763 } else {
764 memcpy(*inode_table + (j * SQFS_METADATA_BLOCK_SIZE),
765 src_table, src_len);
766 }
767
768 /*
769 * Offsets to the decompression destination, to the metadata
770 * buffer 'itb' and to the decompression source, respectively.
771 */
Joao Marcos Costab7fb3c32020-08-19 18:28:41 +0200772
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200773 table_offset += src_len + SQFS_HEADER_SIZE;
774 src_table += src_len + SQFS_HEADER_SIZE;
775 }
776
777free_itb:
778 free(itb);
779
780 return ret;
781}
782
783static int sqfs_read_directory_table(unsigned char **dir_table, u32 **pos_list)
784{
785 u64 start, n_blks, table_offset, table_size;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200786 struct squashfs_super_block *sblk = ctxt.sblk;
Joao Marcos Costab87fd012020-08-18 17:17:22 +0200787 int j, ret = 0, metablks_count = -1;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200788 unsigned char *src_table, *dtb;
789 u32 src_len, dest_offset = 0;
Joao Marcos Costab7fb3c32020-08-19 18:28:41 +0200790 unsigned long dest_len = 0;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200791 bool compressed;
792
Richard Genoud2f9c9852020-11-03 12:11:04 +0100793 *dir_table = NULL;
794 *pos_list = NULL;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200795 /* DIRECTORY TABLE */
796 table_size = get_unaligned_le64(&sblk->fragment_table_start) -
797 get_unaligned_le64(&sblk->directory_table_start);
798 start = get_unaligned_le64(&sblk->directory_table_start) /
799 ctxt.cur_dev->blksz;
800 n_blks = sqfs_calc_n_blks(sblk->directory_table_start,
801 sblk->fragment_table_start, &table_offset);
802
803 /* Allocate a proper sized buffer (dtb) to store the directory table */
804 dtb = malloc_cache_aligned(n_blks * ctxt.cur_dev->blksz);
805 if (!dtb)
806 return -ENOMEM;
807
808 if (sqfs_disk_read(start, n_blks, dtb) < 0)
Richard Genoud2f9c9852020-11-03 12:11:04 +0100809 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200810
811 /* Parse directory table (metadata block) header */
812 ret = sqfs_read_metablock(dtb, table_offset, &compressed, &src_len);
813 if (ret)
Richard Genoud2f9c9852020-11-03 12:11:04 +0100814 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200815
816 /* Calculate total size to store the whole decompressed table */
817 metablks_count = sqfs_count_metablks(dtb, table_offset, table_size);
818 if (metablks_count < 1)
Richard Genoud2f9c9852020-11-03 12:11:04 +0100819 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200820
821 *dir_table = malloc(metablks_count * SQFS_METADATA_BLOCK_SIZE);
822 if (!*dir_table)
Richard Genoud2f9c9852020-11-03 12:11:04 +0100823 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200824
825 *pos_list = malloc(metablks_count * sizeof(u32));
Richard Genoud2f9c9852020-11-03 12:11:04 +0100826 if (!*pos_list)
827 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200828
829 ret = sqfs_get_metablk_pos(*pos_list, dtb, table_offset,
830 metablks_count);
831 if (ret) {
832 metablks_count = -1;
Richard Genoud2f9c9852020-11-03 12:11:04 +0100833 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200834 }
835
836 src_table = dtb + table_offset + SQFS_HEADER_SIZE;
837
838 /* Extract compressed Directory table */
839 dest_offset = 0;
840 for (j = 0; j < metablks_count; j++) {
841 sqfs_read_metablock(dtb, table_offset, &compressed, &src_len);
842 if (compressed) {
843 dest_len = SQFS_METADATA_BLOCK_SIZE;
Joao Marcos Costab87fd012020-08-18 17:17:22 +0200844 ret = sqfs_decompress(&ctxt, *dir_table +
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200845 (j * SQFS_METADATA_BLOCK_SIZE),
846 &dest_len, src_table, src_len);
847 if (ret) {
848 metablks_count = -1;
Richard Genoud2f9c9852020-11-03 12:11:04 +0100849 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200850 }
851
852 if (dest_len < SQFS_METADATA_BLOCK_SIZE) {
853 dest_offset += dest_len;
854 break;
855 }
Joao Marcos Costab7fb3c32020-08-19 18:28:41 +0200856
857 dest_offset += dest_len;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200858 } else {
859 memcpy(*dir_table + (j * SQFS_METADATA_BLOCK_SIZE),
860 src_table, src_len);
861 }
862
863 /*
864 * Offsets to the decompression destination, to the metadata
865 * buffer 'dtb' and to the decompression source, respectively.
866 */
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200867 table_offset += src_len + SQFS_HEADER_SIZE;
868 src_table += src_len + SQFS_HEADER_SIZE;
869 }
870
Richard Genoud2f9c9852020-11-03 12:11:04 +0100871out:
872 if (metablks_count < 1) {
873 free(*dir_table);
874 free(*pos_list);
875 *dir_table = NULL;
876 *pos_list = NULL;
877 }
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200878 free(dtb);
879
880 return metablks_count;
881}
882
883int sqfs_opendir(const char *filename, struct fs_dir_stream **dirsp)
884{
885 unsigned char *inode_table = NULL, *dir_table = NULL;
Richard Genoud32bea5b2020-11-03 12:11:01 +0100886 int j, token_count = 0, ret = 0, metablks_count;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200887 struct squashfs_dir_stream *dirs;
Richard Genoud32bea5b2020-11-03 12:11:01 +0100888 char **token_list = NULL, *path = NULL;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200889 u32 *pos_list = NULL;
890
Heinrich Schuchardte086af82021-05-17 08:21:39 +0200891 dirs = calloc(1, sizeof(*dirs));
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200892 if (!dirs)
893 return -EINVAL;
894
Richard Genoud557f08f2020-11-03 12:11:00 +0100895 /* these should be set to NULL to prevent dangling pointers */
896 dirs->dir_header = NULL;
897 dirs->entry = NULL;
898 dirs->table = NULL;
899 dirs->inode_table = NULL;
900 dirs->dir_table = NULL;
901
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200902 ret = sqfs_read_inode_table(&inode_table);
Richard Genoud557f08f2020-11-03 12:11:00 +0100903 if (ret) {
904 ret = -EINVAL;
Richard Genoud32bea5b2020-11-03 12:11:01 +0100905 goto out;
Richard Genoud557f08f2020-11-03 12:11:00 +0100906 }
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200907
908 metablks_count = sqfs_read_directory_table(&dir_table, &pos_list);
Richard Genoud557f08f2020-11-03 12:11:00 +0100909 if (metablks_count < 1) {
910 ret = -EINVAL;
Richard Genoud32bea5b2020-11-03 12:11:01 +0100911 goto out;
Richard Genoud557f08f2020-11-03 12:11:00 +0100912 }
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200913
914 /* Tokenize filename */
915 token_count = sqfs_count_tokens(filename);
Richard Genoud557f08f2020-11-03 12:11:00 +0100916 if (token_count < 0) {
917 ret = -EINVAL;
Richard Genoud32bea5b2020-11-03 12:11:01 +0100918 goto out;
Richard Genoud557f08f2020-11-03 12:11:00 +0100919 }
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200920
921 path = strdup(filename);
Richard Genoud557f08f2020-11-03 12:11:00 +0100922 if (!path) {
923 ret = -EINVAL;
Richard Genoud32bea5b2020-11-03 12:11:01 +0100924 goto out;
Richard Genoud557f08f2020-11-03 12:11:00 +0100925 }
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200926
927 token_list = malloc(token_count * sizeof(char *));
928 if (!token_list) {
929 ret = -EINVAL;
Richard Genoud32bea5b2020-11-03 12:11:01 +0100930 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200931 }
932
933 /* Fill tokens list */
934 ret = sqfs_tokenize(token_list, token_count, path);
935 if (ret)
Richard Genoud32bea5b2020-11-03 12:11:01 +0100936 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200937 /*
938 * ldir's (extended directory) size is greater than dir, so it works as
939 * a general solution for the malloc size, since 'i' is a union.
940 */
941 dirs->inode_table = inode_table;
942 dirs->dir_table = dir_table;
943 ret = sqfs_search_dir(dirs, token_list, token_count, pos_list,
944 metablks_count);
945 if (ret)
Richard Genoud32bea5b2020-11-03 12:11:01 +0100946 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200947
948 if (le16_to_cpu(dirs->i_dir.inode_type) == SQFS_DIR_TYPE)
949 dirs->size = le16_to_cpu(dirs->i_dir.file_size);
950 else
951 dirs->size = le32_to_cpu(dirs->i_ldir.file_size);
952
953 /* Setup directory header */
954 memcpy(dirs->dir_header, dirs->table, SQFS_DIR_HEADER_SIZE);
955 dirs->entry_count = dirs->dir_header->count + 1;
956 dirs->size -= SQFS_DIR_HEADER_SIZE;
957
958 /* Setup entry */
959 dirs->entry = NULL;
960 dirs->table += SQFS_DIR_HEADER_SIZE;
961
962 *dirsp = (struct fs_dir_stream *)dirs;
963
Richard Genoud32bea5b2020-11-03 12:11:01 +0100964out:
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200965 for (j = 0; j < token_count; j++)
966 free(token_list[j]);
967 free(token_list);
968 free(pos_list);
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200969 free(path);
Richard Genoud32bea5b2020-11-03 12:11:01 +0100970 if (ret) {
Richard Genoud557f08f2020-11-03 12:11:00 +0100971 free(inode_table);
Richard Genoud557f08f2020-11-03 12:11:00 +0100972 free(dirs);
Richard Genoud32bea5b2020-11-03 12:11:01 +0100973 }
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200974
975 return ret;
976}
977
978int sqfs_readdir(struct fs_dir_stream *fs_dirs, struct fs_dirent **dentp)
979{
980 struct squashfs_super_block *sblk = ctxt.sblk;
981 struct squashfs_dir_stream *dirs;
982 struct squashfs_lreg_inode *lreg;
983 struct squashfs_base_inode *base;
984 struct squashfs_reg_inode *reg;
985 int i_number, offset = 0, ret;
986 struct fs_dirent *dent;
987 unsigned char *ipos;
Miquel Raynala8e95fd2022-06-09 16:02:06 +0200988 u16 name_size;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200989
990 dirs = (struct squashfs_dir_stream *)fs_dirs;
991 if (!dirs->size) {
992 *dentp = NULL;
993 return -SQFS_STOP_READDIR;
994 }
995
996 dent = &dirs->dentp;
997
998 if (!dirs->entry_count) {
999 if (dirs->size > SQFS_DIR_HEADER_SIZE) {
1000 dirs->size -= SQFS_DIR_HEADER_SIZE;
1001 } else {
1002 *dentp = NULL;
1003 dirs->size = 0;
1004 return -SQFS_STOP_READDIR;
1005 }
1006
1007 if (dirs->size > SQFS_EMPTY_FILE_SIZE) {
1008 /* Read follow-up (emitted) dir. header */
1009 memcpy(dirs->dir_header, dirs->table,
1010 SQFS_DIR_HEADER_SIZE);
1011 dirs->entry_count = dirs->dir_header->count + 1;
1012 ret = sqfs_read_entry(&dirs->entry, dirs->table +
1013 SQFS_DIR_HEADER_SIZE);
1014 if (ret)
1015 return -SQFS_STOP_READDIR;
1016
1017 dirs->table += SQFS_DIR_HEADER_SIZE;
1018 }
1019 } else {
1020 ret = sqfs_read_entry(&dirs->entry, dirs->table);
1021 if (ret)
1022 return -SQFS_STOP_READDIR;
1023 }
1024
1025 i_number = dirs->dir_header->inode_number + dirs->entry->inode_offset;
1026 ipos = sqfs_find_inode(dirs->inode_table, i_number, sblk->inodes,
1027 sblk->block_size);
1028
1029 base = (struct squashfs_base_inode *)ipos;
1030
1031 /* Set entry type and size */
1032 switch (dirs->entry->type) {
1033 case SQFS_DIR_TYPE:
1034 case SQFS_LDIR_TYPE:
1035 dent->type = FS_DT_DIR;
1036 break;
1037 case SQFS_REG_TYPE:
1038 case SQFS_LREG_TYPE:
1039 /*
1040 * Entries do not differentiate extended from regular types, so
1041 * it needs to be verified manually.
1042 */
1043 if (get_unaligned_le16(&base->inode_type) == SQFS_LREG_TYPE) {
1044 lreg = (struct squashfs_lreg_inode *)ipos;
1045 dent->size = get_unaligned_le64(&lreg->file_size);
1046 } else {
1047 reg = (struct squashfs_reg_inode *)ipos;
1048 dent->size = get_unaligned_le32(&reg->file_size);
1049 }
1050
1051 dent->type = FS_DT_REG;
1052 break;
1053 case SQFS_BLKDEV_TYPE:
1054 case SQFS_CHRDEV_TYPE:
1055 case SQFS_LBLKDEV_TYPE:
1056 case SQFS_LCHRDEV_TYPE:
1057 case SQFS_FIFO_TYPE:
1058 case SQFS_SOCKET_TYPE:
1059 case SQFS_LFIFO_TYPE:
1060 case SQFS_LSOCKET_TYPE:
1061 dent->type = SQFS_MISC_ENTRY_TYPE;
1062 break;
1063 case SQFS_SYMLINK_TYPE:
1064 case SQFS_LSYMLINK_TYPE:
1065 dent->type = FS_DT_LNK;
1066 break;
1067 default:
1068 return -SQFS_STOP_READDIR;
1069 }
1070
Miquel Raynala8e95fd2022-06-09 16:02:06 +02001071 /* Set entry name (capped at FS_DIRENT_NAME_LEN which is a U-Boot limitation) */
1072 name_size = min_t(u16, dirs->entry->name_size + 1, FS_DIRENT_NAME_LEN - 1);
1073 strncpy(dent->name, dirs->entry->name, name_size);
1074 dent->name[name_size] = '\0';
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001075
1076 offset = dirs->entry->name_size + 1 + SQFS_ENTRY_BASE_LENGTH;
1077 dirs->entry_count--;
1078
1079 /* Decrement size to be read */
1080 if (dirs->size > offset)
1081 dirs->size -= offset;
1082 else
1083 dirs->size = 0;
1084
1085 /* Keep a reference to the current entry before incrementing it */
1086 dirs->table += offset;
1087
1088 *dentp = dent;
1089
1090 return 0;
1091}
1092
1093int sqfs_probe(struct blk_desc *fs_dev_desc, struct disk_partition *fs_partition)
1094{
1095 struct squashfs_super_block *sblk;
1096 int ret;
1097
1098 ctxt.cur_dev = fs_dev_desc;
1099 ctxt.cur_part_info = *fs_partition;
1100
1101 ret = sqfs_read_sblk(&sblk);
1102 if (ret)
Richard Genoudcd72bc32020-11-03 12:11:21 +01001103 goto error;
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001104
1105 /* Make sure it has a valid SquashFS magic number*/
1106 if (get_unaligned_le32(&sblk->s_magic) != SQFS_MAGIC_NUMBER) {
Simon Glass9936fe32021-08-18 21:40:27 -06001107 debug("Bad magic number for SquashFS image.\n");
Richard Genoudac7c4db2020-11-03 12:11:19 +01001108 ret = -EINVAL;
1109 goto error;
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001110 }
1111
1112 ctxt.sblk = sblk;
1113
Joao Marcos Costa877576c2020-08-18 17:17:21 +02001114 ret = sqfs_decompressor_init(&ctxt);
Joao Marcos Costa877576c2020-08-18 17:17:21 +02001115 if (ret) {
Richard Genoudac7c4db2020-11-03 12:11:19 +01001116 goto error;
Joao Marcos Costa877576c2020-08-18 17:17:21 +02001117 }
1118
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001119 return 0;
Richard Genoudac7c4db2020-11-03 12:11:19 +01001120error:
1121 ctxt.cur_dev = NULL;
1122 free(ctxt.sblk);
1123 ctxt.sblk = NULL;
1124 return ret;
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001125}
1126
1127static char *sqfs_basename(char *path)
1128{
1129 char *fname;
1130
1131 fname = path + strlen(path) - 1;
1132 while (fname >= path) {
1133 if (*fname == '/') {
1134 fname++;
1135 break;
1136 }
1137
1138 fname--;
1139 }
1140
1141 return fname;
1142}
1143
1144static char *sqfs_dirname(char *path)
1145{
1146 char *fname;
1147
1148 fname = sqfs_basename(path);
1149 --fname;
1150 *fname = '\0';
1151
1152 return path;
1153}
1154
1155/*
1156 * Takes a path to file and splits it in two parts: the filename itself and the
1157 * directory's path, e.g.:
1158 * path: /path/to/file.txt
1159 * file: file.txt
1160 * dir: /path/to
1161 */
1162static int sqfs_split_path(char **file, char **dir, const char *path)
1163{
1164 char *dirc, *basec, *bname, *dname, *tmp_path;
1165 int ret = 0;
1166
Richard Genoud8cf2f022020-11-03 12:11:03 +01001167 *file = NULL;
1168 *dir = NULL;
1169 dirc = NULL;
1170 basec = NULL;
1171 bname = NULL;
1172 dname = NULL;
1173 tmp_path = NULL;
1174
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001175 /* check for first slash in path*/
1176 if (path[0] == '/') {
1177 tmp_path = strdup(path);
Richard Genoud8cf2f022020-11-03 12:11:03 +01001178 if (!tmp_path) {
1179 ret = -ENOMEM;
1180 goto out;
1181 }
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001182 } else {
1183 tmp_path = malloc(strlen(path) + 2);
Richard Genoud8cf2f022020-11-03 12:11:03 +01001184 if (!tmp_path) {
1185 ret = -ENOMEM;
1186 goto out;
1187 }
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001188 tmp_path[0] = '/';
1189 strcpy(tmp_path + 1, path);
1190 }
1191
1192 /* String duplicates */
1193 dirc = strdup(tmp_path);
1194 if (!dirc) {
1195 ret = -ENOMEM;
Richard Genoud8cf2f022020-11-03 12:11:03 +01001196 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001197 }
1198
1199 basec = strdup(tmp_path);
1200 if (!basec) {
1201 ret = -ENOMEM;
Richard Genoud8cf2f022020-11-03 12:11:03 +01001202 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001203 }
1204
1205 dname = sqfs_dirname(dirc);
1206 bname = sqfs_basename(basec);
1207
1208 *file = strdup(bname);
1209
1210 if (!*file) {
1211 ret = -ENOMEM;
Richard Genoud8cf2f022020-11-03 12:11:03 +01001212 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001213 }
1214
1215 if (*dname == '\0') {
1216 *dir = malloc(2);
1217 if (!*dir) {
1218 ret = -ENOMEM;
Richard Genoud8cf2f022020-11-03 12:11:03 +01001219 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001220 }
1221
1222 (*dir)[0] = '/';
1223 (*dir)[1] = '\0';
1224 } else {
1225 *dir = strdup(dname);
1226 if (!*dir) {
1227 ret = -ENOMEM;
Richard Genoud8cf2f022020-11-03 12:11:03 +01001228 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001229 }
1230 }
1231
Richard Genoud8cf2f022020-11-03 12:11:03 +01001232out:
1233 if (ret) {
1234 free(*file);
1235 free(*dir);
1236 *dir = NULL;
1237 *file = NULL;
1238 }
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001239 free(basec);
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001240 free(dirc);
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001241 free(tmp_path);
1242
1243 return ret;
1244}
1245
1246static int sqfs_get_regfile_info(struct squashfs_reg_inode *reg,
1247 struct squashfs_file_info *finfo,
1248 struct squashfs_fragment_block_entry *fentry,
1249 __le32 blksz)
1250{
1251 int datablk_count = 0, ret;
1252
1253 finfo->size = get_unaligned_le32(&reg->file_size);
1254 finfo->offset = get_unaligned_le32(&reg->offset);
1255 finfo->start = get_unaligned_le32(&reg->start_block);
1256 finfo->frag = SQFS_IS_FRAGMENTED(get_unaligned_le32(&reg->fragment));
1257
Joao Marcos Costaf1dfe4a2020-09-11 12:21:06 +02001258 if (finfo->frag && finfo->offset == 0xFFFFFFFF)
Joao Marcos Costab7fb3c32020-08-19 18:28:41 +02001259 return -EINVAL;
1260
Joao Marcos Costaf1dfe4a2020-09-11 12:21:06 +02001261 if (finfo->size < 1 || finfo->start == 0xFFFFFFFF)
1262 return -EINVAL;
1263
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001264 if (finfo->frag) {
1265 datablk_count = finfo->size / le32_to_cpu(blksz);
1266 ret = sqfs_frag_lookup(get_unaligned_le32(&reg->fragment),
1267 fentry);
1268 if (ret < 0)
1269 return -EINVAL;
Joao Marcos Costa8a724fa2021-05-17 18:20:38 -03001270 finfo->comp = ret;
Joao Marcos Costaf1dfe4a2020-09-11 12:21:06 +02001271 if (fentry->size < 1 || fentry->start == 0x7FFFFFFF)
Joao Marcos Costab7fb3c32020-08-19 18:28:41 +02001272 return -EINVAL;
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001273 } else {
1274 datablk_count = DIV_ROUND_UP(finfo->size, le32_to_cpu(blksz));
1275 }
1276
1277 finfo->blk_sizes = malloc(datablk_count * sizeof(u32));
1278 if (!finfo->blk_sizes)
1279 return -ENOMEM;
1280
1281 return datablk_count;
1282}
1283
1284static int sqfs_get_lregfile_info(struct squashfs_lreg_inode *lreg,
1285 struct squashfs_file_info *finfo,
1286 struct squashfs_fragment_block_entry *fentry,
1287 __le32 blksz)
1288{
1289 int datablk_count = 0, ret;
1290
1291 finfo->size = get_unaligned_le64(&lreg->file_size);
1292 finfo->offset = get_unaligned_le32(&lreg->offset);
1293 finfo->start = get_unaligned_le64(&lreg->start_block);
1294 finfo->frag = SQFS_IS_FRAGMENTED(get_unaligned_le32(&lreg->fragment));
1295
Joao Marcos Costaf1dfe4a2020-09-11 12:21:06 +02001296 if (finfo->frag && finfo->offset == 0xFFFFFFFF)
Joao Marcos Costab7fb3c32020-08-19 18:28:41 +02001297 return -EINVAL;
1298
Joao Marcos Costaf1dfe4a2020-09-11 12:21:06 +02001299 if (finfo->size < 1 || finfo->start == 0x7FFFFFFF)
1300 return -EINVAL;
1301
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001302 if (finfo->frag) {
1303 datablk_count = finfo->size / le32_to_cpu(blksz);
1304 ret = sqfs_frag_lookup(get_unaligned_le32(&lreg->fragment),
1305 fentry);
1306 if (ret < 0)
1307 return -EINVAL;
Joao Marcos Costa8a724fa2021-05-17 18:20:38 -03001308 finfo->comp = ret;
Joao Marcos Costaf1dfe4a2020-09-11 12:21:06 +02001309 if (fentry->size < 1 || fentry->start == 0x7FFFFFFF)
Joao Marcos Costab7fb3c32020-08-19 18:28:41 +02001310 return -EINVAL;
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001311 } else {
1312 datablk_count = DIV_ROUND_UP(finfo->size, le32_to_cpu(blksz));
1313 }
1314
1315 finfo->blk_sizes = malloc(datablk_count * sizeof(u32));
1316 if (!finfo->blk_sizes)
1317 return -ENOMEM;
1318
1319 return datablk_count;
1320}
1321
1322int sqfs_read(const char *filename, void *buf, loff_t offset, loff_t len,
1323 loff_t *actread)
1324{
Heinrich Schuchardt8e709432022-04-11 22:54:44 +02001325 char *dir = NULL, *fragment_block, *datablock = NULL;
Richard Genoud0caee472020-11-03 12:11:18 +01001326 char *fragment = NULL, *file = NULL, *resolved, *data;
Campbell Suter46446632020-11-23 15:40:03 +13001327 u64 start, n_blks, table_size, data_offset, table_offset, sparse_size;
Joao Marcos Costab87fd012020-08-18 17:17:22 +02001328 int ret, j, i_number, datablk_count = 0;
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001329 struct squashfs_super_block *sblk = ctxt.sblk;
1330 struct squashfs_fragment_block_entry frag_entry;
1331 struct squashfs_file_info finfo = {0};
1332 struct squashfs_symlink_inode *symlink;
1333 struct fs_dir_stream *dirsp = NULL;
1334 struct squashfs_dir_stream *dirs;
1335 struct squashfs_lreg_inode *lreg;
1336 struct squashfs_base_inode *base;
1337 struct squashfs_reg_inode *reg;
1338 unsigned long dest_len;
1339 struct fs_dirent *dent;
1340 unsigned char *ipos;
1341
1342 *actread = 0;
1343
Richard Genoudcfcb2242020-11-03 12:11:24 +01001344 if (offset) {
1345 /*
1346 * TODO: implement reading at an offset in file
1347 */
1348 printf("Error: reading at a specific offset in a squashfs file is not supported yet.\n");
1349 return -EINVAL;
1350 }
1351
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001352 /*
1353 * sqfs_opendir will uncompress inode and directory tables, and will
1354 * return a pointer to the directory that contains the requested file.
1355 */
1356 sqfs_split_path(&file, &dir, filename);
1357 ret = sqfs_opendir(dir, &dirsp);
1358 if (ret) {
Richard Genoud0caee472020-11-03 12:11:18 +01001359 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001360 }
1361
1362 dirs = (struct squashfs_dir_stream *)dirsp;
1363
1364 /* For now, only regular files are able to be loaded */
1365 while (!sqfs_readdir(dirsp, &dent)) {
1366 ret = strcmp(dent->name, file);
1367 if (!ret)
1368 break;
1369
1370 free(dirs->entry);
Richard Genoud0b8aa992020-11-03 12:11:11 +01001371 dirs->entry = NULL;
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001372 }
1373
1374 if (ret) {
1375 printf("File not found.\n");
1376 *actread = 0;
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001377 ret = -ENOENT;
Richard Genoud0caee472020-11-03 12:11:18 +01001378 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001379 }
1380
1381 i_number = dirs->dir_header->inode_number + dirs->entry->inode_offset;
1382 ipos = sqfs_find_inode(dirs->inode_table, i_number, sblk->inodes,
1383 sblk->block_size);
1384
1385 base = (struct squashfs_base_inode *)ipos;
1386 switch (get_unaligned_le16(&base->inode_type)) {
1387 case SQFS_REG_TYPE:
1388 reg = (struct squashfs_reg_inode *)ipos;
1389 datablk_count = sqfs_get_regfile_info(reg, &finfo, &frag_entry,
1390 sblk->block_size);
1391 if (datablk_count < 0) {
1392 ret = -EINVAL;
Richard Genoud0caee472020-11-03 12:11:18 +01001393 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001394 }
1395
1396 memcpy(finfo.blk_sizes, ipos + sizeof(*reg),
1397 datablk_count * sizeof(u32));
1398 break;
1399 case SQFS_LREG_TYPE:
1400 lreg = (struct squashfs_lreg_inode *)ipos;
1401 datablk_count = sqfs_get_lregfile_info(lreg, &finfo,
1402 &frag_entry,
1403 sblk->block_size);
1404 if (datablk_count < 0) {
1405 ret = -EINVAL;
Richard Genoud0caee472020-11-03 12:11:18 +01001406 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001407 }
1408
1409 memcpy(finfo.blk_sizes, ipos + sizeof(*lreg),
1410 datablk_count * sizeof(u32));
1411 break;
1412 case SQFS_SYMLINK_TYPE:
1413 case SQFS_LSYMLINK_TYPE:
1414 symlink = (struct squashfs_symlink_inode *)ipos;
1415 resolved = sqfs_resolve_symlink(symlink, filename);
1416 ret = sqfs_read(resolved, buf, offset, len, actread);
1417 free(resolved);
Richard Genoud0caee472020-11-03 12:11:18 +01001418 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001419 case SQFS_BLKDEV_TYPE:
1420 case SQFS_CHRDEV_TYPE:
1421 case SQFS_LBLKDEV_TYPE:
1422 case SQFS_LCHRDEV_TYPE:
1423 case SQFS_FIFO_TYPE:
1424 case SQFS_SOCKET_TYPE:
1425 case SQFS_LFIFO_TYPE:
1426 case SQFS_LSOCKET_TYPE:
1427 default:
1428 printf("Unsupported entry type\n");
1429 ret = -EINVAL;
Richard Genoud0caee472020-11-03 12:11:18 +01001430 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001431 }
1432
1433 /* If the user specifies a length, check its sanity */
1434 if (len) {
1435 if (len > finfo.size) {
1436 ret = -EINVAL;
Richard Genoud0caee472020-11-03 12:11:18 +01001437 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001438 }
1439
1440 finfo.size = len;
Richard Genoud6abdf012020-11-03 12:11:23 +01001441 } else {
1442 len = finfo.size;
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001443 }
1444
1445 if (datablk_count) {
1446 data_offset = finfo.start;
1447 datablock = malloc(get_unaligned_le32(&sblk->block_size));
1448 if (!datablock) {
1449 ret = -ENOMEM;
Richard Genoud0caee472020-11-03 12:11:18 +01001450 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001451 }
1452 }
1453
1454 for (j = 0; j < datablk_count; j++) {
Heinrich Schuchardt8e709432022-04-11 22:54:44 +02001455 char *data_buffer;
1456
Sean Nyekjaera8eb9622022-05-12 20:37:14 +02001457 start = lldiv(data_offset, ctxt.cur_dev->blksz);
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001458 table_size = SQFS_BLOCK_SIZE(finfo.blk_sizes[j]);
1459 table_offset = data_offset - (start * ctxt.cur_dev->blksz);
1460 n_blks = DIV_ROUND_UP(table_size + table_offset,
1461 ctxt.cur_dev->blksz);
1462
Campbell Suter46446632020-11-23 15:40:03 +13001463 /* Don't load any data for sparse blocks */
1464 if (finfo.blk_sizes[j] == 0) {
1465 n_blks = 0;
1466 table_offset = 0;
1467 data_buffer = NULL;
1468 data = NULL;
1469 } else {
1470 data_buffer = malloc_cache_aligned(n_blks * ctxt.cur_dev->blksz);
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001471
Campbell Suter46446632020-11-23 15:40:03 +13001472 if (!data_buffer) {
1473 ret = -ENOMEM;
1474 goto out;
1475 }
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001476
Campbell Suter46446632020-11-23 15:40:03 +13001477 ret = sqfs_disk_read(start, n_blks, data_buffer);
1478 if (ret < 0) {
1479 /*
1480 * Possible causes: too many data blocks or too large
1481 * SquashFS block size. Tip: re-compile the SquashFS
1482 * image with mksquashfs's -b <block_size> option.
1483 */
1484 printf("Error: too many data blocks to be read.\n");
1485 goto out;
1486 }
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001487
Campbell Suter46446632020-11-23 15:40:03 +13001488 data = data_buffer + table_offset;
1489 }
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001490
1491 /* Load the data */
Campbell Suter46446632020-11-23 15:40:03 +13001492 if (finfo.blk_sizes[j] == 0) {
1493 /* This is a sparse block */
1494 sparse_size = get_unaligned_le32(&sblk->block_size);
1495 if ((*actread + sparse_size) > len)
1496 sparse_size = len - *actread;
1497 memset(buf + *actread, 0, sparse_size);
1498 *actread += sparse_size;
1499 } else if (SQFS_COMPRESSED_BLOCK(finfo.blk_sizes[j])) {
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001500 dest_len = get_unaligned_le32(&sblk->block_size);
Joao Marcos Costab87fd012020-08-18 17:17:22 +02001501 ret = sqfs_decompress(&ctxt, datablock, &dest_len,
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001502 data, table_size);
1503 if (ret)
Richard Genoud0caee472020-11-03 12:11:18 +01001504 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001505
Richard Genoud6abdf012020-11-03 12:11:23 +01001506 if ((*actread + dest_len) > len)
1507 dest_len = len - *actread;
Richard Genoudcfcb2242020-11-03 12:11:24 +01001508 memcpy(buf + *actread, datablock, dest_len);
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001509 *actread += dest_len;
1510 } else {
Richard Genoud6abdf012020-11-03 12:11:23 +01001511 if ((*actread + table_size) > len)
1512 table_size = len - *actread;
Richard Genoudcfcb2242020-11-03 12:11:24 +01001513 memcpy(buf + *actread, data, table_size);
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001514 *actread += table_size;
1515 }
1516
1517 data_offset += table_size;
Heinrich Schuchardt8e709432022-04-11 22:54:44 +02001518 free(data_buffer);
Richard Genoud6abdf012020-11-03 12:11:23 +01001519 if (*actread >= len)
1520 break;
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001521 }
1522
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001523 /*
1524 * There is no need to continue if the file is not fragmented.
1525 */
1526 if (!finfo.frag) {
1527 ret = 0;
Richard Genoud0caee472020-11-03 12:11:18 +01001528 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001529 }
1530
Sean Nyekjaera8eb9622022-05-12 20:37:14 +02001531 start = lldiv(frag_entry.start, ctxt.cur_dev->blksz);
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001532 table_size = SQFS_BLOCK_SIZE(frag_entry.size);
1533 table_offset = frag_entry.start - (start * ctxt.cur_dev->blksz);
1534 n_blks = DIV_ROUND_UP(table_size + table_offset, ctxt.cur_dev->blksz);
1535
1536 fragment = malloc_cache_aligned(n_blks * ctxt.cur_dev->blksz);
1537
1538 if (!fragment) {
1539 ret = -ENOMEM;
Richard Genoud0caee472020-11-03 12:11:18 +01001540 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001541 }
1542
1543 ret = sqfs_disk_read(start, n_blks, fragment);
1544 if (ret < 0)
Richard Genoud0caee472020-11-03 12:11:18 +01001545 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001546
1547 /* File compressed and fragmented */
1548 if (finfo.frag && finfo.comp) {
1549 dest_len = get_unaligned_le32(&sblk->block_size);
1550 fragment_block = malloc(dest_len);
1551 if (!fragment_block) {
1552 ret = -ENOMEM;
Richard Genoud0caee472020-11-03 12:11:18 +01001553 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001554 }
1555
Joao Marcos Costab87fd012020-08-18 17:17:22 +02001556 ret = sqfs_decompress(&ctxt, fragment_block, &dest_len,
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001557 (void *)fragment + table_offset,
1558 frag_entry.size);
1559 if (ret) {
1560 free(fragment_block);
Richard Genoud0caee472020-11-03 12:11:18 +01001561 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001562 }
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 free(fragment_block);
1568
1569 } else if (finfo.frag && !finfo.comp) {
1570 fragment_block = (void *)fragment + table_offset;
1571
Joao Marcos Costa8a724fa2021-05-17 18:20:38 -03001572 memcpy(buf + *actread, &fragment_block[finfo.offset], finfo.size - *actread);
1573 *actread = finfo.size;
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001574 }
1575
Richard Genoud0caee472020-11-03 12:11:18 +01001576out:
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001577 free(fragment);
Heinrich Schuchardt8e709432022-04-11 22:54:44 +02001578 free(datablock);
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001579 free(file);
1580 free(dir);
Richard Genoud0caee472020-11-03 12:11:18 +01001581 free(finfo.blk_sizes);
Richard Genoud84a5f572020-11-03 12:11:13 +01001582 sqfs_closedir(dirsp);
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001583
1584 return ret;
1585}
1586
1587int sqfs_size(const char *filename, loff_t *size)
1588{
1589 struct squashfs_super_block *sblk = ctxt.sblk;
1590 struct squashfs_symlink_inode *symlink;
1591 struct fs_dir_stream *dirsp = NULL;
1592 struct squashfs_base_inode *base;
1593 struct squashfs_dir_stream *dirs;
1594 struct squashfs_lreg_inode *lreg;
1595 struct squashfs_reg_inode *reg;
1596 char *dir, *file, *resolved;
1597 struct fs_dirent *dent;
1598 unsigned char *ipos;
1599 int ret, i_number;
1600
1601 sqfs_split_path(&file, &dir, filename);
1602 /*
1603 * sqfs_opendir will uncompress inode and directory tables, and will
1604 * return a pointer to the directory that contains the requested file.
1605 */
1606 ret = sqfs_opendir(dir, &dirsp);
1607 if (ret) {
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001608 ret = -EINVAL;
1609 goto free_strings;
1610 }
1611
1612 dirs = (struct squashfs_dir_stream *)dirsp;
1613
1614 while (!sqfs_readdir(dirsp, &dent)) {
1615 ret = strcmp(dent->name, file);
1616 if (!ret)
1617 break;
1618 free(dirs->entry);
Richard Genoudc3dadcb2020-11-03 12:11:09 +01001619 dirs->entry = NULL;
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001620 }
1621
1622 if (ret) {
1623 printf("File not found.\n");
1624 *size = 0;
1625 ret = -EINVAL;
1626 goto free_strings;
1627 }
1628
1629 i_number = dirs->dir_header->inode_number + dirs->entry->inode_offset;
1630 ipos = sqfs_find_inode(dirs->inode_table, i_number, sblk->inodes,
1631 sblk->block_size);
1632 free(dirs->entry);
Richard Genoudc3dadcb2020-11-03 12:11:09 +01001633 dirs->entry = NULL;
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001634
1635 base = (struct squashfs_base_inode *)ipos;
1636 switch (get_unaligned_le16(&base->inode_type)) {
1637 case SQFS_REG_TYPE:
1638 reg = (struct squashfs_reg_inode *)ipos;
1639 *size = get_unaligned_le32(&reg->file_size);
1640 break;
1641 case SQFS_LREG_TYPE:
1642 lreg = (struct squashfs_lreg_inode *)ipos;
1643 *size = get_unaligned_le64(&lreg->file_size);
1644 break;
1645 case SQFS_SYMLINK_TYPE:
1646 case SQFS_LSYMLINK_TYPE:
1647 symlink = (struct squashfs_symlink_inode *)ipos;
1648 resolved = sqfs_resolve_symlink(symlink, filename);
1649 ret = sqfs_size(resolved, size);
1650 free(resolved);
1651 break;
1652 case SQFS_BLKDEV_TYPE:
1653 case SQFS_CHRDEV_TYPE:
1654 case SQFS_LBLKDEV_TYPE:
1655 case SQFS_LCHRDEV_TYPE:
1656 case SQFS_FIFO_TYPE:
1657 case SQFS_SOCKET_TYPE:
1658 case SQFS_LFIFO_TYPE:
1659 case SQFS_LSOCKET_TYPE:
1660 default:
1661 printf("Unable to recover entry's size.\n");
1662 *size = 0;
1663 ret = -EINVAL;
1664 break;
1665 }
1666
1667free_strings:
1668 free(dir);
1669 free(file);
1670
1671 sqfs_closedir(dirsp);
1672
1673 return ret;
1674}
1675
Richard Genoudcfca20c2020-11-03 12:11:26 +01001676int sqfs_exists(const char *filename)
1677{
1678 struct fs_dir_stream *dirsp = NULL;
1679 struct squashfs_dir_stream *dirs;
1680 char *dir, *file;
1681 struct fs_dirent *dent;
1682 int ret;
1683
1684 sqfs_split_path(&file, &dir, filename);
1685 /*
1686 * sqfs_opendir will uncompress inode and directory tables, and will
1687 * return a pointer to the directory that contains the requested file.
1688 */
1689 ret = sqfs_opendir(dir, &dirsp);
1690 if (ret) {
1691 ret = -EINVAL;
1692 goto free_strings;
1693 }
1694
1695 dirs = (struct squashfs_dir_stream *)dirsp;
1696
1697 while (!sqfs_readdir(dirsp, &dent)) {
1698 ret = strcmp(dent->name, file);
1699 if (!ret)
1700 break;
1701 free(dirs->entry);
1702 dirs->entry = NULL;
1703 }
1704
1705 sqfs_closedir(dirsp);
1706
1707free_strings:
1708 free(dir);
1709 free(file);
1710
1711 return ret == 0;
1712}
1713
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001714void sqfs_close(void)
1715{
Richard Genoud1d95a1e2020-11-24 18:07:52 +01001716 sqfs_decompressor_cleanup(&ctxt);
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001717 free(ctxt.sblk);
Richard Genoud1d95a1e2020-11-24 18:07:52 +01001718 ctxt.sblk = NULL;
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001719 ctxt.cur_dev = NULL;
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001720}
1721
1722void sqfs_closedir(struct fs_dir_stream *dirs)
1723{
1724 struct squashfs_dir_stream *sqfs_dirs;
1725
Heinrich Schuchardt836bba42021-02-01 03:28:48 +01001726 if (!dirs)
1727 return;
1728
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001729 sqfs_dirs = (struct squashfs_dir_stream *)dirs;
1730 free(sqfs_dirs->inode_table);
1731 free(sqfs_dirs->dir_table);
1732 free(sqfs_dirs->dir_header);
Richard Genoud2575eb22020-11-03 12:11:02 +01001733 free(sqfs_dirs);
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001734}