blob: b4484fa17f59dc0baab7ad60d130c0624be0bde3 [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>
15#include <linux/byteorder/little_endian.h>
16#include <linux/byteorder/generic.h>
17#include <memalign.h>
18#include <stdlib.h>
19#include <string.h>
20#include <squashfs.h>
21#include <part.h>
22
23#include "sqfs_decompressor.h"
24#include "sqfs_filesystem.h"
25#include "sqfs_utils.h"
26
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
729 *inode_table = malloc(metablks_count * SQFS_METADATA_BLOCK_SIZE);
730 if (!*inode_table) {
731 ret = -ENOMEM;
Lars Weber205783f2022-01-13 14:28:45 +0100732 printf("Error: failed to allocate squashfs inode_table of size %i, increasing CONFIG_SYS_MALLOC_LEN could help\n",
733 metablks_count * SQFS_METADATA_BLOCK_SIZE);
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200734 goto free_itb;
735 }
736
737 src_table = itb + table_offset + SQFS_HEADER_SIZE;
738
739 /* Extract compressed Inode table */
740 for (j = 0; j < metablks_count; j++) {
741 sqfs_read_metablock(itb, table_offset, &compressed, &src_len);
742 if (compressed) {
743 dest_len = SQFS_METADATA_BLOCK_SIZE;
Joao Marcos Costab87fd012020-08-18 17:17:22 +0200744 ret = sqfs_decompress(&ctxt, *inode_table +
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200745 dest_offset, &dest_len,
746 src_table, src_len);
747 if (ret) {
748 free(*inode_table);
Richard Genouda62528d2020-11-03 12:11:07 +0100749 *inode_table = NULL;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200750 goto free_itb;
751 }
752
Joao Marcos Costab7fb3c32020-08-19 18:28:41 +0200753 dest_offset += dest_len;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200754 } else {
755 memcpy(*inode_table + (j * SQFS_METADATA_BLOCK_SIZE),
756 src_table, src_len);
757 }
758
759 /*
760 * Offsets to the decompression destination, to the metadata
761 * buffer 'itb' and to the decompression source, respectively.
762 */
Joao Marcos Costab7fb3c32020-08-19 18:28:41 +0200763
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200764 table_offset += src_len + SQFS_HEADER_SIZE;
765 src_table += src_len + SQFS_HEADER_SIZE;
766 }
767
768free_itb:
769 free(itb);
770
771 return ret;
772}
773
774static int sqfs_read_directory_table(unsigned char **dir_table, u32 **pos_list)
775{
776 u64 start, n_blks, table_offset, table_size;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200777 struct squashfs_super_block *sblk = ctxt.sblk;
Joao Marcos Costab87fd012020-08-18 17:17:22 +0200778 int j, ret = 0, metablks_count = -1;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200779 unsigned char *src_table, *dtb;
780 u32 src_len, dest_offset = 0;
Joao Marcos Costab7fb3c32020-08-19 18:28:41 +0200781 unsigned long dest_len = 0;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200782 bool compressed;
783
Richard Genoud2f9c9852020-11-03 12:11:04 +0100784 *dir_table = NULL;
785 *pos_list = NULL;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200786 /* DIRECTORY TABLE */
787 table_size = get_unaligned_le64(&sblk->fragment_table_start) -
788 get_unaligned_le64(&sblk->directory_table_start);
789 start = get_unaligned_le64(&sblk->directory_table_start) /
790 ctxt.cur_dev->blksz;
791 n_blks = sqfs_calc_n_blks(sblk->directory_table_start,
792 sblk->fragment_table_start, &table_offset);
793
794 /* Allocate a proper sized buffer (dtb) to store the directory table */
795 dtb = malloc_cache_aligned(n_blks * ctxt.cur_dev->blksz);
796 if (!dtb)
797 return -ENOMEM;
798
799 if (sqfs_disk_read(start, n_blks, dtb) < 0)
Richard Genoud2f9c9852020-11-03 12:11:04 +0100800 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200801
802 /* Parse directory table (metadata block) header */
803 ret = sqfs_read_metablock(dtb, table_offset, &compressed, &src_len);
804 if (ret)
Richard Genoud2f9c9852020-11-03 12:11:04 +0100805 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200806
807 /* Calculate total size to store the whole decompressed table */
808 metablks_count = sqfs_count_metablks(dtb, table_offset, table_size);
809 if (metablks_count < 1)
Richard Genoud2f9c9852020-11-03 12:11:04 +0100810 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200811
812 *dir_table = malloc(metablks_count * SQFS_METADATA_BLOCK_SIZE);
813 if (!*dir_table)
Richard Genoud2f9c9852020-11-03 12:11:04 +0100814 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200815
816 *pos_list = malloc(metablks_count * sizeof(u32));
Richard Genoud2f9c9852020-11-03 12:11:04 +0100817 if (!*pos_list)
818 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200819
820 ret = sqfs_get_metablk_pos(*pos_list, dtb, table_offset,
821 metablks_count);
822 if (ret) {
823 metablks_count = -1;
Richard Genoud2f9c9852020-11-03 12:11:04 +0100824 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200825 }
826
827 src_table = dtb + table_offset + SQFS_HEADER_SIZE;
828
829 /* Extract compressed Directory table */
830 dest_offset = 0;
831 for (j = 0; j < metablks_count; j++) {
832 sqfs_read_metablock(dtb, table_offset, &compressed, &src_len);
833 if (compressed) {
834 dest_len = SQFS_METADATA_BLOCK_SIZE;
Joao Marcos Costab87fd012020-08-18 17:17:22 +0200835 ret = sqfs_decompress(&ctxt, *dir_table +
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200836 (j * SQFS_METADATA_BLOCK_SIZE),
837 &dest_len, src_table, src_len);
838 if (ret) {
839 metablks_count = -1;
Richard Genoud2f9c9852020-11-03 12:11:04 +0100840 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200841 }
842
843 if (dest_len < SQFS_METADATA_BLOCK_SIZE) {
844 dest_offset += dest_len;
845 break;
846 }
Joao Marcos Costab7fb3c32020-08-19 18:28:41 +0200847
848 dest_offset += dest_len;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200849 } else {
850 memcpy(*dir_table + (j * SQFS_METADATA_BLOCK_SIZE),
851 src_table, src_len);
852 }
853
854 /*
855 * Offsets to the decompression destination, to the metadata
856 * buffer 'dtb' and to the decompression source, respectively.
857 */
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200858 table_offset += src_len + SQFS_HEADER_SIZE;
859 src_table += src_len + SQFS_HEADER_SIZE;
860 }
861
Richard Genoud2f9c9852020-11-03 12:11:04 +0100862out:
863 if (metablks_count < 1) {
864 free(*dir_table);
865 free(*pos_list);
866 *dir_table = NULL;
867 *pos_list = NULL;
868 }
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200869 free(dtb);
870
871 return metablks_count;
872}
873
874int sqfs_opendir(const char *filename, struct fs_dir_stream **dirsp)
875{
876 unsigned char *inode_table = NULL, *dir_table = NULL;
Richard Genoud32bea5b2020-11-03 12:11:01 +0100877 int j, token_count = 0, ret = 0, metablks_count;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200878 struct squashfs_dir_stream *dirs;
Richard Genoud32bea5b2020-11-03 12:11:01 +0100879 char **token_list = NULL, *path = NULL;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200880 u32 *pos_list = NULL;
881
Heinrich Schuchardte086af82021-05-17 08:21:39 +0200882 dirs = calloc(1, sizeof(*dirs));
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200883 if (!dirs)
884 return -EINVAL;
885
Richard Genoud557f08f2020-11-03 12:11:00 +0100886 /* these should be set to NULL to prevent dangling pointers */
887 dirs->dir_header = NULL;
888 dirs->entry = NULL;
889 dirs->table = NULL;
890 dirs->inode_table = NULL;
891 dirs->dir_table = NULL;
892
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200893 ret = sqfs_read_inode_table(&inode_table);
Richard Genoud557f08f2020-11-03 12:11:00 +0100894 if (ret) {
895 ret = -EINVAL;
Richard Genoud32bea5b2020-11-03 12:11:01 +0100896 goto out;
Richard Genoud557f08f2020-11-03 12:11:00 +0100897 }
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200898
899 metablks_count = sqfs_read_directory_table(&dir_table, &pos_list);
Richard Genoud557f08f2020-11-03 12:11:00 +0100900 if (metablks_count < 1) {
901 ret = -EINVAL;
Richard Genoud32bea5b2020-11-03 12:11:01 +0100902 goto out;
Richard Genoud557f08f2020-11-03 12:11:00 +0100903 }
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200904
905 /* Tokenize filename */
906 token_count = sqfs_count_tokens(filename);
Richard Genoud557f08f2020-11-03 12:11:00 +0100907 if (token_count < 0) {
908 ret = -EINVAL;
Richard Genoud32bea5b2020-11-03 12:11:01 +0100909 goto out;
Richard Genoud557f08f2020-11-03 12:11:00 +0100910 }
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200911
912 path = strdup(filename);
Richard Genoud557f08f2020-11-03 12:11:00 +0100913 if (!path) {
914 ret = -EINVAL;
Richard Genoud32bea5b2020-11-03 12:11:01 +0100915 goto out;
Richard Genoud557f08f2020-11-03 12:11:00 +0100916 }
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200917
918 token_list = malloc(token_count * sizeof(char *));
919 if (!token_list) {
920 ret = -EINVAL;
Richard Genoud32bea5b2020-11-03 12:11:01 +0100921 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200922 }
923
924 /* Fill tokens list */
925 ret = sqfs_tokenize(token_list, token_count, path);
926 if (ret)
Richard Genoud32bea5b2020-11-03 12:11:01 +0100927 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200928 /*
929 * ldir's (extended directory) size is greater than dir, so it works as
930 * a general solution for the malloc size, since 'i' is a union.
931 */
932 dirs->inode_table = inode_table;
933 dirs->dir_table = dir_table;
934 ret = sqfs_search_dir(dirs, token_list, token_count, pos_list,
935 metablks_count);
936 if (ret)
Richard Genoud32bea5b2020-11-03 12:11:01 +0100937 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200938
939 if (le16_to_cpu(dirs->i_dir.inode_type) == SQFS_DIR_TYPE)
940 dirs->size = le16_to_cpu(dirs->i_dir.file_size);
941 else
942 dirs->size = le32_to_cpu(dirs->i_ldir.file_size);
943
944 /* Setup directory header */
945 memcpy(dirs->dir_header, dirs->table, SQFS_DIR_HEADER_SIZE);
946 dirs->entry_count = dirs->dir_header->count + 1;
947 dirs->size -= SQFS_DIR_HEADER_SIZE;
948
949 /* Setup entry */
950 dirs->entry = NULL;
951 dirs->table += SQFS_DIR_HEADER_SIZE;
952
953 *dirsp = (struct fs_dir_stream *)dirs;
954
Richard Genoud32bea5b2020-11-03 12:11:01 +0100955out:
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200956 for (j = 0; j < token_count; j++)
957 free(token_list[j]);
958 free(token_list);
959 free(pos_list);
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200960 free(path);
Richard Genoud32bea5b2020-11-03 12:11:01 +0100961 if (ret) {
Richard Genoud557f08f2020-11-03 12:11:00 +0100962 free(inode_table);
Richard Genoud557f08f2020-11-03 12:11:00 +0100963 free(dirs);
Richard Genoud32bea5b2020-11-03 12:11:01 +0100964 }
Joao Marcos Costa29da3742020-07-30 15:33:47 +0200965
966 return ret;
967}
968
969int sqfs_readdir(struct fs_dir_stream *fs_dirs, struct fs_dirent **dentp)
970{
971 struct squashfs_super_block *sblk = ctxt.sblk;
972 struct squashfs_dir_stream *dirs;
973 struct squashfs_lreg_inode *lreg;
974 struct squashfs_base_inode *base;
975 struct squashfs_reg_inode *reg;
976 int i_number, offset = 0, ret;
977 struct fs_dirent *dent;
978 unsigned char *ipos;
979
980 dirs = (struct squashfs_dir_stream *)fs_dirs;
981 if (!dirs->size) {
982 *dentp = NULL;
983 return -SQFS_STOP_READDIR;
984 }
985
986 dent = &dirs->dentp;
987
988 if (!dirs->entry_count) {
989 if (dirs->size > SQFS_DIR_HEADER_SIZE) {
990 dirs->size -= SQFS_DIR_HEADER_SIZE;
991 } else {
992 *dentp = NULL;
993 dirs->size = 0;
994 return -SQFS_STOP_READDIR;
995 }
996
997 if (dirs->size > SQFS_EMPTY_FILE_SIZE) {
998 /* Read follow-up (emitted) dir. header */
999 memcpy(dirs->dir_header, dirs->table,
1000 SQFS_DIR_HEADER_SIZE);
1001 dirs->entry_count = dirs->dir_header->count + 1;
1002 ret = sqfs_read_entry(&dirs->entry, dirs->table +
1003 SQFS_DIR_HEADER_SIZE);
1004 if (ret)
1005 return -SQFS_STOP_READDIR;
1006
1007 dirs->table += SQFS_DIR_HEADER_SIZE;
1008 }
1009 } else {
1010 ret = sqfs_read_entry(&dirs->entry, dirs->table);
1011 if (ret)
1012 return -SQFS_STOP_READDIR;
1013 }
1014
1015 i_number = dirs->dir_header->inode_number + dirs->entry->inode_offset;
1016 ipos = sqfs_find_inode(dirs->inode_table, i_number, sblk->inodes,
1017 sblk->block_size);
1018
1019 base = (struct squashfs_base_inode *)ipos;
1020
1021 /* Set entry type and size */
1022 switch (dirs->entry->type) {
1023 case SQFS_DIR_TYPE:
1024 case SQFS_LDIR_TYPE:
1025 dent->type = FS_DT_DIR;
1026 break;
1027 case SQFS_REG_TYPE:
1028 case SQFS_LREG_TYPE:
1029 /*
1030 * Entries do not differentiate extended from regular types, so
1031 * it needs to be verified manually.
1032 */
1033 if (get_unaligned_le16(&base->inode_type) == SQFS_LREG_TYPE) {
1034 lreg = (struct squashfs_lreg_inode *)ipos;
1035 dent->size = get_unaligned_le64(&lreg->file_size);
1036 } else {
1037 reg = (struct squashfs_reg_inode *)ipos;
1038 dent->size = get_unaligned_le32(&reg->file_size);
1039 }
1040
1041 dent->type = FS_DT_REG;
1042 break;
1043 case SQFS_BLKDEV_TYPE:
1044 case SQFS_CHRDEV_TYPE:
1045 case SQFS_LBLKDEV_TYPE:
1046 case SQFS_LCHRDEV_TYPE:
1047 case SQFS_FIFO_TYPE:
1048 case SQFS_SOCKET_TYPE:
1049 case SQFS_LFIFO_TYPE:
1050 case SQFS_LSOCKET_TYPE:
1051 dent->type = SQFS_MISC_ENTRY_TYPE;
1052 break;
1053 case SQFS_SYMLINK_TYPE:
1054 case SQFS_LSYMLINK_TYPE:
1055 dent->type = FS_DT_LNK;
1056 break;
1057 default:
1058 return -SQFS_STOP_READDIR;
1059 }
1060
1061 /* Set entry name */
1062 strncpy(dent->name, dirs->entry->name, dirs->entry->name_size + 1);
1063 dent->name[dirs->entry->name_size + 1] = '\0';
1064
1065 offset = dirs->entry->name_size + 1 + SQFS_ENTRY_BASE_LENGTH;
1066 dirs->entry_count--;
1067
1068 /* Decrement size to be read */
1069 if (dirs->size > offset)
1070 dirs->size -= offset;
1071 else
1072 dirs->size = 0;
1073
1074 /* Keep a reference to the current entry before incrementing it */
1075 dirs->table += offset;
1076
1077 *dentp = dent;
1078
1079 return 0;
1080}
1081
1082int sqfs_probe(struct blk_desc *fs_dev_desc, struct disk_partition *fs_partition)
1083{
1084 struct squashfs_super_block *sblk;
1085 int ret;
1086
1087 ctxt.cur_dev = fs_dev_desc;
1088 ctxt.cur_part_info = *fs_partition;
1089
1090 ret = sqfs_read_sblk(&sblk);
1091 if (ret)
Richard Genoudcd72bc32020-11-03 12:11:21 +01001092 goto error;
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001093
1094 /* Make sure it has a valid SquashFS magic number*/
1095 if (get_unaligned_le32(&sblk->s_magic) != SQFS_MAGIC_NUMBER) {
Simon Glass9936fe32021-08-18 21:40:27 -06001096 debug("Bad magic number for SquashFS image.\n");
Richard Genoudac7c4db2020-11-03 12:11:19 +01001097 ret = -EINVAL;
1098 goto error;
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001099 }
1100
1101 ctxt.sblk = sblk;
1102
Joao Marcos Costa877576c2020-08-18 17:17:21 +02001103 ret = sqfs_decompressor_init(&ctxt);
Joao Marcos Costa877576c2020-08-18 17:17:21 +02001104 if (ret) {
Richard Genoudac7c4db2020-11-03 12:11:19 +01001105 goto error;
Joao Marcos Costa877576c2020-08-18 17:17:21 +02001106 }
1107
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001108 return 0;
Richard Genoudac7c4db2020-11-03 12:11:19 +01001109error:
1110 ctxt.cur_dev = NULL;
1111 free(ctxt.sblk);
1112 ctxt.sblk = NULL;
1113 return ret;
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001114}
1115
1116static char *sqfs_basename(char *path)
1117{
1118 char *fname;
1119
1120 fname = path + strlen(path) - 1;
1121 while (fname >= path) {
1122 if (*fname == '/') {
1123 fname++;
1124 break;
1125 }
1126
1127 fname--;
1128 }
1129
1130 return fname;
1131}
1132
1133static char *sqfs_dirname(char *path)
1134{
1135 char *fname;
1136
1137 fname = sqfs_basename(path);
1138 --fname;
1139 *fname = '\0';
1140
1141 return path;
1142}
1143
1144/*
1145 * Takes a path to file and splits it in two parts: the filename itself and the
1146 * directory's path, e.g.:
1147 * path: /path/to/file.txt
1148 * file: file.txt
1149 * dir: /path/to
1150 */
1151static int sqfs_split_path(char **file, char **dir, const char *path)
1152{
1153 char *dirc, *basec, *bname, *dname, *tmp_path;
1154 int ret = 0;
1155
Richard Genoud8cf2f022020-11-03 12:11:03 +01001156 *file = NULL;
1157 *dir = NULL;
1158 dirc = NULL;
1159 basec = NULL;
1160 bname = NULL;
1161 dname = NULL;
1162 tmp_path = NULL;
1163
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001164 /* check for first slash in path*/
1165 if (path[0] == '/') {
1166 tmp_path = strdup(path);
Richard Genoud8cf2f022020-11-03 12:11:03 +01001167 if (!tmp_path) {
1168 ret = -ENOMEM;
1169 goto out;
1170 }
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001171 } else {
1172 tmp_path = malloc(strlen(path) + 2);
Richard Genoud8cf2f022020-11-03 12:11:03 +01001173 if (!tmp_path) {
1174 ret = -ENOMEM;
1175 goto out;
1176 }
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001177 tmp_path[0] = '/';
1178 strcpy(tmp_path + 1, path);
1179 }
1180
1181 /* String duplicates */
1182 dirc = strdup(tmp_path);
1183 if (!dirc) {
1184 ret = -ENOMEM;
Richard Genoud8cf2f022020-11-03 12:11:03 +01001185 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001186 }
1187
1188 basec = strdup(tmp_path);
1189 if (!basec) {
1190 ret = -ENOMEM;
Richard Genoud8cf2f022020-11-03 12:11:03 +01001191 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001192 }
1193
1194 dname = sqfs_dirname(dirc);
1195 bname = sqfs_basename(basec);
1196
1197 *file = strdup(bname);
1198
1199 if (!*file) {
1200 ret = -ENOMEM;
Richard Genoud8cf2f022020-11-03 12:11:03 +01001201 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001202 }
1203
1204 if (*dname == '\0') {
1205 *dir = malloc(2);
1206 if (!*dir) {
1207 ret = -ENOMEM;
Richard Genoud8cf2f022020-11-03 12:11:03 +01001208 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001209 }
1210
1211 (*dir)[0] = '/';
1212 (*dir)[1] = '\0';
1213 } else {
1214 *dir = strdup(dname);
1215 if (!*dir) {
1216 ret = -ENOMEM;
Richard Genoud8cf2f022020-11-03 12:11:03 +01001217 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001218 }
1219 }
1220
Richard Genoud8cf2f022020-11-03 12:11:03 +01001221out:
1222 if (ret) {
1223 free(*file);
1224 free(*dir);
1225 *dir = NULL;
1226 *file = NULL;
1227 }
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001228 free(basec);
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001229 free(dirc);
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001230 free(tmp_path);
1231
1232 return ret;
1233}
1234
1235static int sqfs_get_regfile_info(struct squashfs_reg_inode *reg,
1236 struct squashfs_file_info *finfo,
1237 struct squashfs_fragment_block_entry *fentry,
1238 __le32 blksz)
1239{
1240 int datablk_count = 0, ret;
1241
1242 finfo->size = get_unaligned_le32(&reg->file_size);
1243 finfo->offset = get_unaligned_le32(&reg->offset);
1244 finfo->start = get_unaligned_le32(&reg->start_block);
1245 finfo->frag = SQFS_IS_FRAGMENTED(get_unaligned_le32(&reg->fragment));
1246
Joao Marcos Costaf1dfe4a2020-09-11 12:21:06 +02001247 if (finfo->frag && finfo->offset == 0xFFFFFFFF)
Joao Marcos Costab7fb3c32020-08-19 18:28:41 +02001248 return -EINVAL;
1249
Joao Marcos Costaf1dfe4a2020-09-11 12:21:06 +02001250 if (finfo->size < 1 || finfo->start == 0xFFFFFFFF)
1251 return -EINVAL;
1252
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001253 if (finfo->frag) {
1254 datablk_count = finfo->size / le32_to_cpu(blksz);
1255 ret = sqfs_frag_lookup(get_unaligned_le32(&reg->fragment),
1256 fentry);
1257 if (ret < 0)
1258 return -EINVAL;
Joao Marcos Costa8a724fa2021-05-17 18:20:38 -03001259 finfo->comp = ret;
Joao Marcos Costaf1dfe4a2020-09-11 12:21:06 +02001260 if (fentry->size < 1 || fentry->start == 0x7FFFFFFF)
Joao Marcos Costab7fb3c32020-08-19 18:28:41 +02001261 return -EINVAL;
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001262 } else {
1263 datablk_count = DIV_ROUND_UP(finfo->size, le32_to_cpu(blksz));
1264 }
1265
1266 finfo->blk_sizes = malloc(datablk_count * sizeof(u32));
1267 if (!finfo->blk_sizes)
1268 return -ENOMEM;
1269
1270 return datablk_count;
1271}
1272
1273static int sqfs_get_lregfile_info(struct squashfs_lreg_inode *lreg,
1274 struct squashfs_file_info *finfo,
1275 struct squashfs_fragment_block_entry *fentry,
1276 __le32 blksz)
1277{
1278 int datablk_count = 0, ret;
1279
1280 finfo->size = get_unaligned_le64(&lreg->file_size);
1281 finfo->offset = get_unaligned_le32(&lreg->offset);
1282 finfo->start = get_unaligned_le64(&lreg->start_block);
1283 finfo->frag = SQFS_IS_FRAGMENTED(get_unaligned_le32(&lreg->fragment));
1284
Joao Marcos Costaf1dfe4a2020-09-11 12:21:06 +02001285 if (finfo->frag && finfo->offset == 0xFFFFFFFF)
Joao Marcos Costab7fb3c32020-08-19 18:28:41 +02001286 return -EINVAL;
1287
Joao Marcos Costaf1dfe4a2020-09-11 12:21:06 +02001288 if (finfo->size < 1 || finfo->start == 0x7FFFFFFF)
1289 return -EINVAL;
1290
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001291 if (finfo->frag) {
1292 datablk_count = finfo->size / le32_to_cpu(blksz);
1293 ret = sqfs_frag_lookup(get_unaligned_le32(&lreg->fragment),
1294 fentry);
1295 if (ret < 0)
1296 return -EINVAL;
Joao Marcos Costa8a724fa2021-05-17 18:20:38 -03001297 finfo->comp = ret;
Joao Marcos Costaf1dfe4a2020-09-11 12:21:06 +02001298 if (fentry->size < 1 || fentry->start == 0x7FFFFFFF)
Joao Marcos Costab7fb3c32020-08-19 18:28:41 +02001299 return -EINVAL;
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001300 } else {
1301 datablk_count = DIV_ROUND_UP(finfo->size, le32_to_cpu(blksz));
1302 }
1303
1304 finfo->blk_sizes = malloc(datablk_count * sizeof(u32));
1305 if (!finfo->blk_sizes)
1306 return -ENOMEM;
1307
1308 return datablk_count;
1309}
1310
1311int sqfs_read(const char *filename, void *buf, loff_t offset, loff_t len,
1312 loff_t *actread)
1313{
Heinrich Schuchardt8e709432022-04-11 22:54:44 +02001314 char *dir = NULL, *fragment_block, *datablock = NULL;
Richard Genoud0caee472020-11-03 12:11:18 +01001315 char *fragment = NULL, *file = NULL, *resolved, *data;
Campbell Suter46446632020-11-23 15:40:03 +13001316 u64 start, n_blks, table_size, data_offset, table_offset, sparse_size;
Joao Marcos Costab87fd012020-08-18 17:17:22 +02001317 int ret, j, i_number, datablk_count = 0;
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001318 struct squashfs_super_block *sblk = ctxt.sblk;
1319 struct squashfs_fragment_block_entry frag_entry;
1320 struct squashfs_file_info finfo = {0};
1321 struct squashfs_symlink_inode *symlink;
1322 struct fs_dir_stream *dirsp = NULL;
1323 struct squashfs_dir_stream *dirs;
1324 struct squashfs_lreg_inode *lreg;
1325 struct squashfs_base_inode *base;
1326 struct squashfs_reg_inode *reg;
1327 unsigned long dest_len;
1328 struct fs_dirent *dent;
1329 unsigned char *ipos;
1330
1331 *actread = 0;
1332
Richard Genoudcfcb2242020-11-03 12:11:24 +01001333 if (offset) {
1334 /*
1335 * TODO: implement reading at an offset in file
1336 */
1337 printf("Error: reading at a specific offset in a squashfs file is not supported yet.\n");
1338 return -EINVAL;
1339 }
1340
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001341 /*
1342 * sqfs_opendir will uncompress inode and directory tables, and will
1343 * return a pointer to the directory that contains the requested file.
1344 */
1345 sqfs_split_path(&file, &dir, filename);
1346 ret = sqfs_opendir(dir, &dirsp);
1347 if (ret) {
Richard Genoud0caee472020-11-03 12:11:18 +01001348 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001349 }
1350
1351 dirs = (struct squashfs_dir_stream *)dirsp;
1352
1353 /* For now, only regular files are able to be loaded */
1354 while (!sqfs_readdir(dirsp, &dent)) {
1355 ret = strcmp(dent->name, file);
1356 if (!ret)
1357 break;
1358
1359 free(dirs->entry);
Richard Genoud0b8aa992020-11-03 12:11:11 +01001360 dirs->entry = NULL;
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001361 }
1362
1363 if (ret) {
1364 printf("File not found.\n");
1365 *actread = 0;
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001366 ret = -ENOENT;
Richard Genoud0caee472020-11-03 12:11:18 +01001367 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001368 }
1369
1370 i_number = dirs->dir_header->inode_number + dirs->entry->inode_offset;
1371 ipos = sqfs_find_inode(dirs->inode_table, i_number, sblk->inodes,
1372 sblk->block_size);
1373
1374 base = (struct squashfs_base_inode *)ipos;
1375 switch (get_unaligned_le16(&base->inode_type)) {
1376 case SQFS_REG_TYPE:
1377 reg = (struct squashfs_reg_inode *)ipos;
1378 datablk_count = sqfs_get_regfile_info(reg, &finfo, &frag_entry,
1379 sblk->block_size);
1380 if (datablk_count < 0) {
1381 ret = -EINVAL;
Richard Genoud0caee472020-11-03 12:11:18 +01001382 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001383 }
1384
1385 memcpy(finfo.blk_sizes, ipos + sizeof(*reg),
1386 datablk_count * sizeof(u32));
1387 break;
1388 case SQFS_LREG_TYPE:
1389 lreg = (struct squashfs_lreg_inode *)ipos;
1390 datablk_count = sqfs_get_lregfile_info(lreg, &finfo,
1391 &frag_entry,
1392 sblk->block_size);
1393 if (datablk_count < 0) {
1394 ret = -EINVAL;
Richard Genoud0caee472020-11-03 12:11:18 +01001395 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001396 }
1397
1398 memcpy(finfo.blk_sizes, ipos + sizeof(*lreg),
1399 datablk_count * sizeof(u32));
1400 break;
1401 case SQFS_SYMLINK_TYPE:
1402 case SQFS_LSYMLINK_TYPE:
1403 symlink = (struct squashfs_symlink_inode *)ipos;
1404 resolved = sqfs_resolve_symlink(symlink, filename);
1405 ret = sqfs_read(resolved, buf, offset, len, actread);
1406 free(resolved);
Richard Genoud0caee472020-11-03 12:11:18 +01001407 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001408 case SQFS_BLKDEV_TYPE:
1409 case SQFS_CHRDEV_TYPE:
1410 case SQFS_LBLKDEV_TYPE:
1411 case SQFS_LCHRDEV_TYPE:
1412 case SQFS_FIFO_TYPE:
1413 case SQFS_SOCKET_TYPE:
1414 case SQFS_LFIFO_TYPE:
1415 case SQFS_LSOCKET_TYPE:
1416 default:
1417 printf("Unsupported entry type\n");
1418 ret = -EINVAL;
Richard Genoud0caee472020-11-03 12:11:18 +01001419 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001420 }
1421
1422 /* If the user specifies a length, check its sanity */
1423 if (len) {
1424 if (len > finfo.size) {
1425 ret = -EINVAL;
Richard Genoud0caee472020-11-03 12:11:18 +01001426 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001427 }
1428
1429 finfo.size = len;
Richard Genoud6abdf012020-11-03 12:11:23 +01001430 } else {
1431 len = finfo.size;
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001432 }
1433
1434 if (datablk_count) {
1435 data_offset = finfo.start;
1436 datablock = malloc(get_unaligned_le32(&sblk->block_size));
1437 if (!datablock) {
1438 ret = -ENOMEM;
Richard Genoud0caee472020-11-03 12:11:18 +01001439 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001440 }
1441 }
1442
1443 for (j = 0; j < datablk_count; j++) {
Heinrich Schuchardt8e709432022-04-11 22:54:44 +02001444 char *data_buffer;
1445
Sean Nyekjaera8eb9622022-05-12 20:37:14 +02001446 start = lldiv(data_offset, ctxt.cur_dev->blksz);
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001447 table_size = SQFS_BLOCK_SIZE(finfo.blk_sizes[j]);
1448 table_offset = data_offset - (start * ctxt.cur_dev->blksz);
1449 n_blks = DIV_ROUND_UP(table_size + table_offset,
1450 ctxt.cur_dev->blksz);
1451
Campbell Suter46446632020-11-23 15:40:03 +13001452 /* Don't load any data for sparse blocks */
1453 if (finfo.blk_sizes[j] == 0) {
1454 n_blks = 0;
1455 table_offset = 0;
1456 data_buffer = NULL;
1457 data = NULL;
1458 } else {
1459 data_buffer = malloc_cache_aligned(n_blks * ctxt.cur_dev->blksz);
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001460
Campbell Suter46446632020-11-23 15:40:03 +13001461 if (!data_buffer) {
1462 ret = -ENOMEM;
1463 goto out;
1464 }
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001465
Campbell Suter46446632020-11-23 15:40:03 +13001466 ret = sqfs_disk_read(start, n_blks, data_buffer);
1467 if (ret < 0) {
1468 /*
1469 * Possible causes: too many data blocks or too large
1470 * SquashFS block size. Tip: re-compile the SquashFS
1471 * image with mksquashfs's -b <block_size> option.
1472 */
1473 printf("Error: too many data blocks to be read.\n");
1474 goto out;
1475 }
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001476
Campbell Suter46446632020-11-23 15:40:03 +13001477 data = data_buffer + table_offset;
1478 }
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001479
1480 /* Load the data */
Campbell Suter46446632020-11-23 15:40:03 +13001481 if (finfo.blk_sizes[j] == 0) {
1482 /* This is a sparse block */
1483 sparse_size = get_unaligned_le32(&sblk->block_size);
1484 if ((*actread + sparse_size) > len)
1485 sparse_size = len - *actread;
1486 memset(buf + *actread, 0, sparse_size);
1487 *actread += sparse_size;
1488 } else if (SQFS_COMPRESSED_BLOCK(finfo.blk_sizes[j])) {
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001489 dest_len = get_unaligned_le32(&sblk->block_size);
Joao Marcos Costab87fd012020-08-18 17:17:22 +02001490 ret = sqfs_decompress(&ctxt, datablock, &dest_len,
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001491 data, table_size);
1492 if (ret)
Richard Genoud0caee472020-11-03 12:11:18 +01001493 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001494
Richard Genoud6abdf012020-11-03 12:11:23 +01001495 if ((*actread + dest_len) > len)
1496 dest_len = len - *actread;
Richard Genoudcfcb2242020-11-03 12:11:24 +01001497 memcpy(buf + *actread, datablock, dest_len);
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001498 *actread += dest_len;
1499 } else {
Richard Genoud6abdf012020-11-03 12:11:23 +01001500 if ((*actread + table_size) > len)
1501 table_size = len - *actread;
Richard Genoudcfcb2242020-11-03 12:11:24 +01001502 memcpy(buf + *actread, data, table_size);
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001503 *actread += table_size;
1504 }
1505
1506 data_offset += table_size;
Heinrich Schuchardt8e709432022-04-11 22:54:44 +02001507 free(data_buffer);
Richard Genoud6abdf012020-11-03 12:11:23 +01001508 if (*actread >= len)
1509 break;
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001510 }
1511
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001512 /*
1513 * There is no need to continue if the file is not fragmented.
1514 */
1515 if (!finfo.frag) {
1516 ret = 0;
Richard Genoud0caee472020-11-03 12:11:18 +01001517 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001518 }
1519
Sean Nyekjaera8eb9622022-05-12 20:37:14 +02001520 start = lldiv(frag_entry.start, ctxt.cur_dev->blksz);
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001521 table_size = SQFS_BLOCK_SIZE(frag_entry.size);
1522 table_offset = frag_entry.start - (start * ctxt.cur_dev->blksz);
1523 n_blks = DIV_ROUND_UP(table_size + table_offset, ctxt.cur_dev->blksz);
1524
1525 fragment = malloc_cache_aligned(n_blks * ctxt.cur_dev->blksz);
1526
1527 if (!fragment) {
1528 ret = -ENOMEM;
Richard Genoud0caee472020-11-03 12:11:18 +01001529 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001530 }
1531
1532 ret = sqfs_disk_read(start, n_blks, fragment);
1533 if (ret < 0)
Richard Genoud0caee472020-11-03 12:11:18 +01001534 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001535
1536 /* File compressed and fragmented */
1537 if (finfo.frag && finfo.comp) {
1538 dest_len = get_unaligned_le32(&sblk->block_size);
1539 fragment_block = malloc(dest_len);
1540 if (!fragment_block) {
1541 ret = -ENOMEM;
Richard Genoud0caee472020-11-03 12:11:18 +01001542 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001543 }
1544
Joao Marcos Costab87fd012020-08-18 17:17:22 +02001545 ret = sqfs_decompress(&ctxt, fragment_block, &dest_len,
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001546 (void *)fragment + table_offset,
1547 frag_entry.size);
1548 if (ret) {
1549 free(fragment_block);
Richard Genoud0caee472020-11-03 12:11:18 +01001550 goto out;
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001551 }
1552
Joao Marcos Costa8a724fa2021-05-17 18:20:38 -03001553 memcpy(buf + *actread, &fragment_block[finfo.offset], finfo.size - *actread);
1554 *actread = finfo.size;
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001555
1556 free(fragment_block);
1557
1558 } else if (finfo.frag && !finfo.comp) {
1559 fragment_block = (void *)fragment + table_offset;
1560
Joao Marcos Costa8a724fa2021-05-17 18:20:38 -03001561 memcpy(buf + *actread, &fragment_block[finfo.offset], finfo.size - *actread);
1562 *actread = finfo.size;
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001563 }
1564
Richard Genoud0caee472020-11-03 12:11:18 +01001565out:
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001566 free(fragment);
Heinrich Schuchardt8e709432022-04-11 22:54:44 +02001567 free(datablock);
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001568 free(file);
1569 free(dir);
Richard Genoud0caee472020-11-03 12:11:18 +01001570 free(finfo.blk_sizes);
Richard Genoud84a5f572020-11-03 12:11:13 +01001571 sqfs_closedir(dirsp);
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001572
1573 return ret;
1574}
1575
1576int sqfs_size(const char *filename, loff_t *size)
1577{
1578 struct squashfs_super_block *sblk = ctxt.sblk;
1579 struct squashfs_symlink_inode *symlink;
1580 struct fs_dir_stream *dirsp = NULL;
1581 struct squashfs_base_inode *base;
1582 struct squashfs_dir_stream *dirs;
1583 struct squashfs_lreg_inode *lreg;
1584 struct squashfs_reg_inode *reg;
1585 char *dir, *file, *resolved;
1586 struct fs_dirent *dent;
1587 unsigned char *ipos;
1588 int ret, i_number;
1589
1590 sqfs_split_path(&file, &dir, filename);
1591 /*
1592 * sqfs_opendir will uncompress inode and directory tables, and will
1593 * return a pointer to the directory that contains the requested file.
1594 */
1595 ret = sqfs_opendir(dir, &dirsp);
1596 if (ret) {
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001597 ret = -EINVAL;
1598 goto free_strings;
1599 }
1600
1601 dirs = (struct squashfs_dir_stream *)dirsp;
1602
1603 while (!sqfs_readdir(dirsp, &dent)) {
1604 ret = strcmp(dent->name, file);
1605 if (!ret)
1606 break;
1607 free(dirs->entry);
Richard Genoudc3dadcb2020-11-03 12:11:09 +01001608 dirs->entry = NULL;
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001609 }
1610
1611 if (ret) {
1612 printf("File not found.\n");
1613 *size = 0;
1614 ret = -EINVAL;
1615 goto free_strings;
1616 }
1617
1618 i_number = dirs->dir_header->inode_number + dirs->entry->inode_offset;
1619 ipos = sqfs_find_inode(dirs->inode_table, i_number, sblk->inodes,
1620 sblk->block_size);
1621 free(dirs->entry);
Richard Genoudc3dadcb2020-11-03 12:11:09 +01001622 dirs->entry = NULL;
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001623
1624 base = (struct squashfs_base_inode *)ipos;
1625 switch (get_unaligned_le16(&base->inode_type)) {
1626 case SQFS_REG_TYPE:
1627 reg = (struct squashfs_reg_inode *)ipos;
1628 *size = get_unaligned_le32(&reg->file_size);
1629 break;
1630 case SQFS_LREG_TYPE:
1631 lreg = (struct squashfs_lreg_inode *)ipos;
1632 *size = get_unaligned_le64(&lreg->file_size);
1633 break;
1634 case SQFS_SYMLINK_TYPE:
1635 case SQFS_LSYMLINK_TYPE:
1636 symlink = (struct squashfs_symlink_inode *)ipos;
1637 resolved = sqfs_resolve_symlink(symlink, filename);
1638 ret = sqfs_size(resolved, size);
1639 free(resolved);
1640 break;
1641 case SQFS_BLKDEV_TYPE:
1642 case SQFS_CHRDEV_TYPE:
1643 case SQFS_LBLKDEV_TYPE:
1644 case SQFS_LCHRDEV_TYPE:
1645 case SQFS_FIFO_TYPE:
1646 case SQFS_SOCKET_TYPE:
1647 case SQFS_LFIFO_TYPE:
1648 case SQFS_LSOCKET_TYPE:
1649 default:
1650 printf("Unable to recover entry's size.\n");
1651 *size = 0;
1652 ret = -EINVAL;
1653 break;
1654 }
1655
1656free_strings:
1657 free(dir);
1658 free(file);
1659
1660 sqfs_closedir(dirsp);
1661
1662 return ret;
1663}
1664
Richard Genoudcfca20c2020-11-03 12:11:26 +01001665int sqfs_exists(const char *filename)
1666{
1667 struct fs_dir_stream *dirsp = NULL;
1668 struct squashfs_dir_stream *dirs;
1669 char *dir, *file;
1670 struct fs_dirent *dent;
1671 int ret;
1672
1673 sqfs_split_path(&file, &dir, filename);
1674 /*
1675 * sqfs_opendir will uncompress inode and directory tables, and will
1676 * return a pointer to the directory that contains the requested file.
1677 */
1678 ret = sqfs_opendir(dir, &dirsp);
1679 if (ret) {
1680 ret = -EINVAL;
1681 goto free_strings;
1682 }
1683
1684 dirs = (struct squashfs_dir_stream *)dirsp;
1685
1686 while (!sqfs_readdir(dirsp, &dent)) {
1687 ret = strcmp(dent->name, file);
1688 if (!ret)
1689 break;
1690 free(dirs->entry);
1691 dirs->entry = NULL;
1692 }
1693
1694 sqfs_closedir(dirsp);
1695
1696free_strings:
1697 free(dir);
1698 free(file);
1699
1700 return ret == 0;
1701}
1702
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001703void sqfs_close(void)
1704{
Richard Genoud1d95a1e2020-11-24 18:07:52 +01001705 sqfs_decompressor_cleanup(&ctxt);
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001706 free(ctxt.sblk);
Richard Genoud1d95a1e2020-11-24 18:07:52 +01001707 ctxt.sblk = NULL;
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001708 ctxt.cur_dev = NULL;
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001709}
1710
1711void sqfs_closedir(struct fs_dir_stream *dirs)
1712{
1713 struct squashfs_dir_stream *sqfs_dirs;
1714
Heinrich Schuchardt836bba42021-02-01 03:28:48 +01001715 if (!dirs)
1716 return;
1717
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001718 sqfs_dirs = (struct squashfs_dir_stream *)dirs;
1719 free(sqfs_dirs->inode_table);
1720 free(sqfs_dirs->dir_table);
1721 free(sqfs_dirs->dir_header);
Richard Genoud2575eb22020-11-03 12:11:02 +01001722 free(sqfs_dirs);
Joao Marcos Costa29da3742020-07-30 15:33:47 +02001723}