blob: 1430e671a5a83d7b90ea7acb3bbd344b46a276ec [file] [log] [blame]
Joao Marcos Costac5100612020-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 Nyekjaer92080c62022-05-12 20:37:14 +020011#include <div64.h>
Joao Marcos Costac5100612020-07-30 15:33:47 +020012#include <errno.h>
13#include <fs.h>
14#include <linux/types.h>
Pali Rohár9320db02022-04-06 23:31:53 +020015#include <asm/byteorder.h>
Miquel Raynal7f7fb992022-06-27 12:20:03 +020016#include <linux/compat.h>
Joao Marcos Costac5100612020-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 Costac5100612020-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 Schuchardt84378d52022-05-10 21:53:25 +020053 *sblk = NULL;
Joao Marcos Costac5100612020-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 Revsbechaeea67f2022-12-01 16:30:32 +010090 start_ = lldiv(le64_to_cpu(start), ctxt.cur_dev->blksz);
Joao Marcos Costac5100612020-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 Oberhollenzerbf48dde2022-12-25 11:05:24 +0100103 u64 start, end, exp_tbl, n_blks, src_len, table_offset, start_block;
Joao Marcos Costac5100612020-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 Costacdc11442020-08-18 17:17:22 +0200109 u16 header;
Joao Marcos Costac5100612020-07-30 15:33:47 +0200110
Richard Genoudc9b8e862020-11-03 12:11:15 +0100111 metadata_buffer = NULL;
112 entries = NULL;
113 table = NULL;
114
Joao Marcos Costac5100612020-07-30 15:33:47 +0200115 if (inode_fragment_index >= get_unaligned_le32(&sblk->fragments))
116 return -EINVAL;
117
David Oberhollenzerbf48dde2022-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 Costac5100612020-07-30 15:33:47 +0200125 n_blks = sqfs_calc_n_blks(sblk->fragment_table_start,
David Oberhollenzerbf48dde2022-12-25 11:05:24 +0100126 cpu_to_le64(end), &table_offset);
127
128 start /= ctxt.cur_dev->blksz;
Joao Marcos Costac5100612020-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 Genoudc9b8e862020-11-03 12:11:15 +0100132 if (!table) {
133 ret = -ENOMEM;
134 goto out;
135 }
Joao Marcos Costac5100612020-07-30 15:33:47 +0200136
137 if (sqfs_disk_read(start, n_blks, table) < 0) {
Richard Genoudc9b8e862020-11-03 12:11:15 +0100138 ret = -EINVAL;
139 goto out;
Joao Marcos Costac5100612020-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 Genoudc9b8e862020-11-03 12:11:15 +0100159 goto out;
Joao Marcos Costac5100612020-07-30 15:33:47 +0200160 }
161
162 if (sqfs_disk_read(start, n_blks, metadata_buffer) < 0) {
163 ret = -EINVAL;
Richard Genoudc9b8e862020-11-03 12:11:15 +0100164 goto out;
Joao Marcos Costac5100612020-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 Costaa7dc37d2020-09-11 12:21:06 +0200171 if (!metadata || !header) {
Joao Marcos Costac9875a52020-08-19 18:28:41 +0200172 ret = -ENOMEM;
Richard Genoudc9b8e862020-11-03 12:11:15 +0100173 goto out;
Joao Marcos Costac9875a52020-08-19 18:28:41 +0200174 }
175
Joao Marcos Costac5100612020-07-30 15:33:47 +0200176 entries = malloc(SQFS_METADATA_BLOCK_SIZE);
177 if (!entries) {
178 ret = -ENOMEM;
Richard Genoudc9b8e862020-11-03 12:11:15 +0100179 goto out;
Joao Marcos Costac5100612020-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 Costacdc11442020-08-18 17:17:22 +0200185 ret = sqfs_decompress(&ctxt, entries, &dest_len, metadata,
Joao Marcos Costac5100612020-07-30 15:33:47 +0200186 src_len);
187 if (ret) {
188 ret = -EINVAL;
Richard Genoudc9b8e862020-11-03 12:11:15 +0100189 goto out;
Joao Marcos Costac5100612020-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 Genoudc9b8e862020-11-03 12:11:15 +0100198out:
Joao Marcos Costac5100612020-07-30 15:33:47 +0200199 free(entries);
Joao Marcos Costac5100612020-07-30 15:33:47 +0200200 free(metadata_buffer);
Joao Marcos Costac5100612020-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 Genouddc3312c2020-11-03 12:11:08 +0100257 if (!result)
258 return NULL;
259
Joao Marcos Costac5100612020-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 Costac9875a52020-08-19 18:28:41 +0200295 int i, j, ret = 0;
Joao Marcos Costac5100612020-07-30 15:33:47 +0200296 char *aux, *strc;
Joao Marcos Costac5100612020-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 Costac9875a52020-08-19 18:28:41 +0200305 ret = -ENOMEM;
306 goto free_strc;
Joao Marcos Costac5100612020-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 Costac9875a52020-08-19 18:28:41 +0200315 ret = -ENOMEM;
316 goto free_strc;
Joao Marcos Costac5100612020-07-30 15:33:47 +0200317 }
318 }
319 }
320
Joao Marcos Costac9875a52020-08-19 18:28:41 +0200321free_strc:
Joao Marcos Costac5100612020-07-30 15:33:47 +0200322 free(strc);
323
Joao Marcos Costac9875a52020-08-19 18:28:41 +0200324 return ret;
Joao Marcos Costac5100612020-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 Genoud33686802020-11-03 12:11:17 +0100351 base_tokens = NULL;
352 rel_tokens = NULL;
353
Joao Marcos Costac5100612020-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 Genoud33686802020-11-03 12:11:17 +0100360 base_tokens = calloc(bc, sizeof(char *));
Joao Marcos Costac5100612020-07-30 15:33:47 +0200361 if (!base_tokens)
362 return NULL;
363
Richard Genoud33686802020-11-03 12:11:17 +0100364 rel_tokens = calloc(rc, sizeof(char *));
Joao Marcos Costac5100612020-07-30 15:33:47 +0200365 if (!rel_tokens)
Richard Genoud33686802020-11-03 12:11:17 +0100366 goto out;
Joao Marcos Costac5100612020-07-30 15:33:47 +0200367
368 /* Fill token lists */
369 ret = sqfs_tokenize(base_tokens, bc, base);
370 if (ret)
Richard Genoud33686802020-11-03 12:11:17 +0100371 goto out;
Joao Marcos Costac5100612020-07-30 15:33:47 +0200372
Richard Genoud53db0e22020-11-03 12:11:16 +0100373 ret = sqfs_tokenize(rel_tokens, rc, rel);
Joao Marcos Costac5100612020-07-30 15:33:47 +0200374 if (ret)
Richard Genoud33686802020-11-03 12:11:17 +0100375 goto out;
Joao Marcos Costac5100612020-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 Genoud33686802020-11-03 12:11:17 +0100386 goto out;
Joao Marcos Costac5100612020-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 Genoud33686802020-11-03 12:11:17 +0100397 goto out;
Joao Marcos Costac5100612020-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 Genoud33686802020-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 Costac5100612020-07-30 15:33:47 +0200413 free(rel_tokens);
Joao Marcos Costac5100612020-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
425 sz = get_unaligned_le32(&sym->symlink_size);
426 target = malloc(sz + 1);
427 if (!target)
428 return NULL;
429
430 /*
431 * There is no trailling null byte in the symlink's target path, so a
432 * copy is made and a '\0' is added at its end.
433 */
434 target[sz] = '\0';
435 /* Get target name (relative path) */
436 strncpy(target, sym->symlink, sz);
437
438 /* Relative -> absolute path conversion */
439 resolved = sqfs_get_abs_path(base_path, target);
440
441 free(target);
442
443 return resolved;
444}
445
446/*
447 * m_list contains each metadata block's position, and m_count is the number of
448 * elements of m_list. Those metadata blocks come from the compressed directory
449 * table.
450 */
451static int sqfs_search_dir(struct squashfs_dir_stream *dirs, char **token_list,
452 int token_count, u32 *m_list, int m_count)
453{
454 struct squashfs_super_block *sblk = ctxt.sblk;
455 char *path, *target, **sym_tokens, *res, *rem;
Richard Genoudcd545912020-11-03 12:11:06 +0100456 int j, ret = 0, new_inode_number, offset;
Joao Marcos Costac5100612020-07-30 15:33:47 +0200457 struct squashfs_symlink_inode *sym;
Joao Marcos Costaa7dc37d2020-09-11 12:21:06 +0200458 struct squashfs_ldir_inode *ldir;
Joao Marcos Costac5100612020-07-30 15:33:47 +0200459 struct squashfs_dir_inode *dir;
460 struct fs_dir_stream *dirsp;
461 struct fs_dirent *dent;
462 unsigned char *table;
463
Richard Genoudcd545912020-11-03 12:11:06 +0100464 res = NULL;
465 rem = NULL;
466 path = NULL;
467 target = NULL;
468 sym_tokens = NULL;
469
Joao Marcos Costac5100612020-07-30 15:33:47 +0200470 dirsp = (struct fs_dir_stream *)dirs;
471
472 /* Start by root inode */
473 table = sqfs_find_inode(dirs->inode_table, le32_to_cpu(sblk->inodes),
474 sblk->inodes, sblk->block_size);
475
Joao Marcos Costac5100612020-07-30 15:33:47 +0200476 dir = (struct squashfs_dir_inode *)table;
Joao Marcos Costaa7dc37d2020-09-11 12:21:06 +0200477 ldir = (struct squashfs_ldir_inode *)table;
Joao Marcos Costac5100612020-07-30 15:33:47 +0200478
479 /* get directory offset in directory table */
480 offset = sqfs_dir_offset(table, m_list, m_count);
481 dirs->table = &dirs->dir_table[offset];
482
483 /* Setup directory header */
484 dirs->dir_header = malloc(SQFS_DIR_HEADER_SIZE);
485 if (!dirs->dir_header)
486 return -ENOMEM;
487
488 memcpy(dirs->dir_header, dirs->table, SQFS_DIR_HEADER_SIZE);
489
490 /* Initialize squashfs_dir_stream members */
491 dirs->table += SQFS_DIR_HEADER_SIZE;
492 dirs->size = get_unaligned_le16(&dir->file_size) - SQFS_DIR_HEADER_SIZE;
493 dirs->entry_count = dirs->dir_header->count + 1;
494
495 /* No path given -> root directory */
496 if (!strcmp(token_list[0], "/")) {
497 dirs->table = &dirs->dir_table[offset];
498 memcpy(&dirs->i_dir, dir, sizeof(*dir));
499 return 0;
500 }
501
502 for (j = 0; j < token_count; j++) {
503 if (!sqfs_is_dir(get_unaligned_le16(&dir->inode_type))) {
504 printf("** Cannot find directory. **\n");
Richard Genoudcd545912020-11-03 12:11:06 +0100505 ret = -EINVAL;
506 goto out;
Joao Marcos Costac5100612020-07-30 15:33:47 +0200507 }
508
509 while (!sqfs_readdir(dirsp, &dent)) {
510 ret = strcmp(dent->name, token_list[j]);
511 if (!ret)
512 break;
513 free(dirs->entry);
Richard Genoud01e71ec2020-11-03 12:11:05 +0100514 dirs->entry = NULL;
Joao Marcos Costac5100612020-07-30 15:33:47 +0200515 }
516
517 if (ret) {
518 printf("** Cannot find directory. **\n");
Richard Genoudcd545912020-11-03 12:11:06 +0100519 ret = -EINVAL;
520 goto out;
Joao Marcos Costac5100612020-07-30 15:33:47 +0200521 }
522
523 /* Redefine inode as the found token */
524 new_inode_number = dirs->entry->inode_offset +
525 dirs->dir_header->inode_number;
526
527 /* Get reference to inode in the inode table */
528 table = sqfs_find_inode(dirs->inode_table, new_inode_number,
529 sblk->inodes, sblk->block_size);
530 dir = (struct squashfs_dir_inode *)table;
531
532 /* Check for symbolic link and inode type sanity */
533 if (get_unaligned_le16(&dir->inode_type) == SQFS_SYMLINK_TYPE) {
534 sym = (struct squashfs_symlink_inode *)table;
535 /* Get first j + 1 tokens */
536 path = sqfs_concat_tokens(token_list, j + 1);
Richard Genoudcd545912020-11-03 12:11:06 +0100537 if (!path) {
538 ret = -ENOMEM;
539 goto out;
540 }
Joao Marcos Costac5100612020-07-30 15:33:47 +0200541 /* Resolve for these tokens */
542 target = sqfs_resolve_symlink(sym, path);
Richard Genoudcd545912020-11-03 12:11:06 +0100543 if (!target) {
544 ret = -ENOMEM;
545 goto out;
546 }
Joao Marcos Costac5100612020-07-30 15:33:47 +0200547 /* Join remaining tokens */
548 rem = sqfs_concat_tokens(token_list + j + 1, token_count -
549 j - 1);
Richard Genoudcd545912020-11-03 12:11:06 +0100550 if (!rem) {
551 ret = -ENOMEM;
552 goto out;
553 }
Joao Marcos Costac5100612020-07-30 15:33:47 +0200554 /* Concatenate remaining tokens and symlink's target */
555 res = malloc(strlen(rem) + strlen(target) + 1);
Richard Genoudcd545912020-11-03 12:11:06 +0100556 if (!res) {
557 ret = -ENOMEM;
558 goto out;
559 }
Joao Marcos Costac5100612020-07-30 15:33:47 +0200560 strcpy(res, target);
561 res[strlen(target)] = '/';
562 strcpy(res + strlen(target) + 1, rem);
563 token_count = sqfs_count_tokens(res);
564
Richard Genoudcd545912020-11-03 12:11:06 +0100565 if (token_count < 0) {
566 ret = -EINVAL;
567 goto out;
568 }
Joao Marcos Costac5100612020-07-30 15:33:47 +0200569
570 sym_tokens = malloc(token_count * sizeof(char *));
Richard Genoudcd545912020-11-03 12:11:06 +0100571 if (!sym_tokens) {
572 ret = -EINVAL;
573 goto out;
574 }
Joao Marcos Costac5100612020-07-30 15:33:47 +0200575
576 /* Fill tokens list */
577 ret = sqfs_tokenize(sym_tokens, token_count, res);
Richard Genoudcd545912020-11-03 12:11:06 +0100578 if (ret) {
579 ret = -EINVAL;
580 goto out;
581 }
Joao Marcos Costac5100612020-07-30 15:33:47 +0200582 free(dirs->entry);
Richard Genoud01e71ec2020-11-03 12:11:05 +0100583 dirs->entry = NULL;
Joao Marcos Costac5100612020-07-30 15:33:47 +0200584
585 ret = sqfs_search_dir(dirs, sym_tokens, token_count,
586 m_list, m_count);
Richard Genoudcd545912020-11-03 12:11:06 +0100587 goto out;
Joao Marcos Costac5100612020-07-30 15:33:47 +0200588 } else if (!sqfs_is_dir(get_unaligned_le16(&dir->inode_type))) {
589 printf("** Cannot find directory. **\n");
590 free(dirs->entry);
Richard Genoud01e71ec2020-11-03 12:11:05 +0100591 dirs->entry = NULL;
Richard Genoudcd545912020-11-03 12:11:06 +0100592 ret = -EINVAL;
593 goto out;
Joao Marcos Costac5100612020-07-30 15:33:47 +0200594 }
595
596 /* Check if it is an extended dir. */
597 if (get_unaligned_le16(&dir->inode_type) == SQFS_LDIR_TYPE)
598 ldir = (struct squashfs_ldir_inode *)table;
599
600 /* Get dir. offset into the directory table */
601 offset = sqfs_dir_offset(table, m_list, m_count);
602 dirs->table = &dirs->dir_table[offset];
603
604 /* Copy directory header */
605 memcpy(dirs->dir_header, &dirs->dir_table[offset],
606 SQFS_DIR_HEADER_SIZE);
607
608 /* Check for empty directory */
609 if (sqfs_is_empty_dir(table)) {
610 printf("Empty directory.\n");
611 free(dirs->entry);
Richard Genoud01e71ec2020-11-03 12:11:05 +0100612 dirs->entry = NULL;
Richard Genoudcd545912020-11-03 12:11:06 +0100613 ret = SQFS_EMPTY_DIR;
614 goto out;
Joao Marcos Costac5100612020-07-30 15:33:47 +0200615 }
616
617 dirs->table += SQFS_DIR_HEADER_SIZE;
618 dirs->size = get_unaligned_le16(&dir->file_size);
619 dirs->entry_count = dirs->dir_header->count + 1;
620 dirs->size -= SQFS_DIR_HEADER_SIZE;
621 free(dirs->entry);
Richard Genoud01e71ec2020-11-03 12:11:05 +0100622 dirs->entry = NULL;
Joao Marcos Costac5100612020-07-30 15:33:47 +0200623 }
624
625 offset = sqfs_dir_offset(table, m_list, m_count);
626 dirs->table = &dirs->dir_table[offset];
627
628 if (get_unaligned_le16(&dir->inode_type) == SQFS_DIR_TYPE)
629 memcpy(&dirs->i_dir, dir, sizeof(*dir));
630 else
631 memcpy(&dirs->i_ldir, ldir, sizeof(*ldir));
632
Richard Genoudcd545912020-11-03 12:11:06 +0100633out:
634 free(res);
635 free(rem);
636 free(path);
637 free(target);
638 free(sym_tokens);
639 return ret;
Joao Marcos Costac5100612020-07-30 15:33:47 +0200640}
641
642/*
643 * Inode and directory tables are stored as a series of metadata blocks, and
644 * given the compressed size of this table, we can calculate how much metadata
645 * blocks are needed to store the result of the decompression, since a
646 * decompressed metadata block should have a size of 8KiB.
647 */
648static int sqfs_count_metablks(void *table, u32 offset, int table_size)
649{
650 int count = 0, cur_size = 0, ret;
651 u32 data_size;
652 bool comp;
653
654 do {
655 ret = sqfs_read_metablock(table, offset + cur_size, &comp,
656 &data_size);
657 if (ret)
658 return -EINVAL;
659 cur_size += data_size + SQFS_HEADER_SIZE;
660 count++;
661 } while (cur_size < table_size);
662
663 return count;
664}
665
666/*
667 * Storing the metadata blocks header's positions will be useful while looking
668 * for an entry in the directory table, using the reference (index and offset)
669 * given by its inode.
670 */
671static int sqfs_get_metablk_pos(u32 *pos_list, void *table, u32 offset,
672 int metablks_count)
673{
674 u32 data_size, cur_size = 0;
675 int j, ret = 0;
676 bool comp;
677
678 if (!metablks_count)
679 return -EINVAL;
680
681 for (j = 0; j < metablks_count; j++) {
682 ret = sqfs_read_metablock(table, offset + cur_size, &comp,
683 &data_size);
684 if (ret)
685 return -EINVAL;
686
687 cur_size += data_size + SQFS_HEADER_SIZE;
688 pos_list[j] = cur_size;
689 }
690
691 return ret;
692}
693
694static int sqfs_read_inode_table(unsigned char **inode_table)
695{
696 struct squashfs_super_block *sblk = ctxt.sblk;
697 u64 start, n_blks, table_offset, table_size;
Joao Marcos Costacdc11442020-08-18 17:17:22 +0200698 int j, ret = 0, metablks_count;
Joao Marcos Costac5100612020-07-30 15:33:47 +0200699 unsigned char *src_table, *itb;
700 u32 src_len, dest_offset = 0;
Joao Marcos Costac9875a52020-08-19 18:28:41 +0200701 unsigned long dest_len = 0;
Joao Marcos Costac5100612020-07-30 15:33:47 +0200702 bool compressed;
703
Joao Marcos Costac5100612020-07-30 15:33:47 +0200704 table_size = get_unaligned_le64(&sblk->directory_table_start) -
705 get_unaligned_le64(&sblk->inode_table_start);
706 start = get_unaligned_le64(&sblk->inode_table_start) /
707 ctxt.cur_dev->blksz;
708 n_blks = sqfs_calc_n_blks(sblk->inode_table_start,
709 sblk->directory_table_start, &table_offset);
710
711 /* Allocate a proper sized buffer (itb) to store the inode table */
712 itb = malloc_cache_aligned(n_blks * ctxt.cur_dev->blksz);
713 if (!itb)
714 return -ENOMEM;
715
716 if (sqfs_disk_read(start, n_blks, itb) < 0) {
717 ret = -EINVAL;
718 goto free_itb;
719 }
720
721 /* Parse inode table (metadata block) header */
722 ret = sqfs_read_metablock(itb, table_offset, &compressed, &src_len);
723 if (ret) {
724 ret = -EINVAL;
725 goto free_itb;
726 }
727
728 /* Calculate size to store the whole decompressed table */
729 metablks_count = sqfs_count_metablks(itb, table_offset, table_size);
730 if (metablks_count < 1) {
731 ret = -EINVAL;
732 goto free_itb;
733 }
734
Miquel Raynal7f7fb992022-06-27 12:20:03 +0200735 *inode_table = kcalloc(metablks_count, SQFS_METADATA_BLOCK_SIZE,
736 GFP_KERNEL);
Joao Marcos Costac5100612020-07-30 15:33:47 +0200737 if (!*inode_table) {
738 ret = -ENOMEM;
Lars Weber1e69db52022-01-13 14:28:45 +0100739 printf("Error: failed to allocate squashfs inode_table of size %i, increasing CONFIG_SYS_MALLOC_LEN could help\n",
740 metablks_count * SQFS_METADATA_BLOCK_SIZE);
Joao Marcos Costac5100612020-07-30 15:33:47 +0200741 goto free_itb;
742 }
743
744 src_table = itb + table_offset + SQFS_HEADER_SIZE;
745
746 /* Extract compressed Inode table */
747 for (j = 0; j < metablks_count; j++) {
748 sqfs_read_metablock(itb, table_offset, &compressed, &src_len);
749 if (compressed) {
750 dest_len = SQFS_METADATA_BLOCK_SIZE;
Joao Marcos Costacdc11442020-08-18 17:17:22 +0200751 ret = sqfs_decompress(&ctxt, *inode_table +
Joao Marcos Costac5100612020-07-30 15:33:47 +0200752 dest_offset, &dest_len,
753 src_table, src_len);
754 if (ret) {
755 free(*inode_table);
Richard Genoud4c83d272020-11-03 12:11:07 +0100756 *inode_table = NULL;
Joao Marcos Costac5100612020-07-30 15:33:47 +0200757 goto free_itb;
758 }
759
Joao Marcos Costac9875a52020-08-19 18:28:41 +0200760 dest_offset += dest_len;
Joao Marcos Costac5100612020-07-30 15:33:47 +0200761 } else {
762 memcpy(*inode_table + (j * SQFS_METADATA_BLOCK_SIZE),
763 src_table, src_len);
764 }
765
766 /*
767 * Offsets to the decompression destination, to the metadata
768 * buffer 'itb' and to the decompression source, respectively.
769 */
Joao Marcos Costac9875a52020-08-19 18:28:41 +0200770
Joao Marcos Costac5100612020-07-30 15:33:47 +0200771 table_offset += src_len + SQFS_HEADER_SIZE;
772 src_table += src_len + SQFS_HEADER_SIZE;
773 }
774
775free_itb:
776 free(itb);
777
778 return ret;
779}
780
781static int sqfs_read_directory_table(unsigned char **dir_table, u32 **pos_list)
782{
783 u64 start, n_blks, table_offset, table_size;
Joao Marcos Costac5100612020-07-30 15:33:47 +0200784 struct squashfs_super_block *sblk = ctxt.sblk;
Joao Marcos Costacdc11442020-08-18 17:17:22 +0200785 int j, ret = 0, metablks_count = -1;
Joao Marcos Costac5100612020-07-30 15:33:47 +0200786 unsigned char *src_table, *dtb;
787 u32 src_len, dest_offset = 0;
Joao Marcos Costac9875a52020-08-19 18:28:41 +0200788 unsigned long dest_len = 0;
Joao Marcos Costac5100612020-07-30 15:33:47 +0200789 bool compressed;
790
Richard Genoud7d23b2c2020-11-03 12:11:04 +0100791 *dir_table = NULL;
792 *pos_list = NULL;
Joao Marcos Costac5100612020-07-30 15:33:47 +0200793 /* DIRECTORY TABLE */
794 table_size = get_unaligned_le64(&sblk->fragment_table_start) -
795 get_unaligned_le64(&sblk->directory_table_start);
796 start = get_unaligned_le64(&sblk->directory_table_start) /
797 ctxt.cur_dev->blksz;
798 n_blks = sqfs_calc_n_blks(sblk->directory_table_start,
799 sblk->fragment_table_start, &table_offset);
800
801 /* Allocate a proper sized buffer (dtb) to store the directory table */
802 dtb = malloc_cache_aligned(n_blks * ctxt.cur_dev->blksz);
803 if (!dtb)
804 return -ENOMEM;
805
806 if (sqfs_disk_read(start, n_blks, dtb) < 0)
Richard Genoud7d23b2c2020-11-03 12:11:04 +0100807 goto out;
Joao Marcos Costac5100612020-07-30 15:33:47 +0200808
809 /* Parse directory table (metadata block) header */
810 ret = sqfs_read_metablock(dtb, table_offset, &compressed, &src_len);
811 if (ret)
Richard Genoud7d23b2c2020-11-03 12:11:04 +0100812 goto out;
Joao Marcos Costac5100612020-07-30 15:33:47 +0200813
814 /* Calculate total size to store the whole decompressed table */
815 metablks_count = sqfs_count_metablks(dtb, table_offset, table_size);
816 if (metablks_count < 1)
Richard Genoud7d23b2c2020-11-03 12:11:04 +0100817 goto out;
Joao Marcos Costac5100612020-07-30 15:33:47 +0200818
819 *dir_table = malloc(metablks_count * SQFS_METADATA_BLOCK_SIZE);
820 if (!*dir_table)
Richard Genoud7d23b2c2020-11-03 12:11:04 +0100821 goto out;
Joao Marcos Costac5100612020-07-30 15:33:47 +0200822
823 *pos_list = malloc(metablks_count * sizeof(u32));
Richard Genoud7d23b2c2020-11-03 12:11:04 +0100824 if (!*pos_list)
825 goto out;
Joao Marcos Costac5100612020-07-30 15:33:47 +0200826
827 ret = sqfs_get_metablk_pos(*pos_list, dtb, table_offset,
828 metablks_count);
829 if (ret) {
830 metablks_count = -1;
Richard Genoud7d23b2c2020-11-03 12:11:04 +0100831 goto out;
Joao Marcos Costac5100612020-07-30 15:33:47 +0200832 }
833
834 src_table = dtb + table_offset + SQFS_HEADER_SIZE;
835
836 /* Extract compressed Directory table */
837 dest_offset = 0;
838 for (j = 0; j < metablks_count; j++) {
839 sqfs_read_metablock(dtb, table_offset, &compressed, &src_len);
840 if (compressed) {
841 dest_len = SQFS_METADATA_BLOCK_SIZE;
Joao Marcos Costacdc11442020-08-18 17:17:22 +0200842 ret = sqfs_decompress(&ctxt, *dir_table +
Joao Marcos Costac5100612020-07-30 15:33:47 +0200843 (j * SQFS_METADATA_BLOCK_SIZE),
844 &dest_len, src_table, src_len);
845 if (ret) {
846 metablks_count = -1;
Richard Genoud7d23b2c2020-11-03 12:11:04 +0100847 goto out;
Joao Marcos Costac5100612020-07-30 15:33:47 +0200848 }
849
850 if (dest_len < SQFS_METADATA_BLOCK_SIZE) {
851 dest_offset += dest_len;
852 break;
853 }
Joao Marcos Costac9875a52020-08-19 18:28:41 +0200854
855 dest_offset += dest_len;
Joao Marcos Costac5100612020-07-30 15:33:47 +0200856 } else {
857 memcpy(*dir_table + (j * SQFS_METADATA_BLOCK_SIZE),
858 src_table, src_len);
859 }
860
861 /*
862 * Offsets to the decompression destination, to the metadata
863 * buffer 'dtb' and to the decompression source, respectively.
864 */
Joao Marcos Costac5100612020-07-30 15:33:47 +0200865 table_offset += src_len + SQFS_HEADER_SIZE;
866 src_table += src_len + SQFS_HEADER_SIZE;
867 }
868
Richard Genoud7d23b2c2020-11-03 12:11:04 +0100869out:
870 if (metablks_count < 1) {
871 free(*dir_table);
872 free(*pos_list);
873 *dir_table = NULL;
874 *pos_list = NULL;
875 }
Joao Marcos Costac5100612020-07-30 15:33:47 +0200876 free(dtb);
877
878 return metablks_count;
879}
880
881int sqfs_opendir(const char *filename, struct fs_dir_stream **dirsp)
882{
883 unsigned char *inode_table = NULL, *dir_table = NULL;
Richard Genoudea1b1652020-11-03 12:11:01 +0100884 int j, token_count = 0, ret = 0, metablks_count;
Joao Marcos Costac5100612020-07-30 15:33:47 +0200885 struct squashfs_dir_stream *dirs;
Richard Genoudea1b1652020-11-03 12:11:01 +0100886 char **token_list = NULL, *path = NULL;
Joao Marcos Costac5100612020-07-30 15:33:47 +0200887 u32 *pos_list = NULL;
888
Heinrich Schuchardt53ba2c22021-05-17 08:21:39 +0200889 dirs = calloc(1, sizeof(*dirs));
Joao Marcos Costac5100612020-07-30 15:33:47 +0200890 if (!dirs)
891 return -EINVAL;
892
Richard Genoudf2687682020-11-03 12:11:00 +0100893 /* these should be set to NULL to prevent dangling pointers */
894 dirs->dir_header = NULL;
895 dirs->entry = NULL;
896 dirs->table = NULL;
897 dirs->inode_table = NULL;
898 dirs->dir_table = NULL;
899
Joao Marcos Costac5100612020-07-30 15:33:47 +0200900 ret = sqfs_read_inode_table(&inode_table);
Richard Genoudf2687682020-11-03 12:11:00 +0100901 if (ret) {
902 ret = -EINVAL;
Richard Genoudea1b1652020-11-03 12:11:01 +0100903 goto out;
Richard Genoudf2687682020-11-03 12:11:00 +0100904 }
Joao Marcos Costac5100612020-07-30 15:33:47 +0200905
906 metablks_count = sqfs_read_directory_table(&dir_table, &pos_list);
Richard Genoudf2687682020-11-03 12:11:00 +0100907 if (metablks_count < 1) {
908 ret = -EINVAL;
Richard Genoudea1b1652020-11-03 12:11:01 +0100909 goto out;
Richard Genoudf2687682020-11-03 12:11:00 +0100910 }
Joao Marcos Costac5100612020-07-30 15:33:47 +0200911
912 /* Tokenize filename */
913 token_count = sqfs_count_tokens(filename);
Richard Genoudf2687682020-11-03 12:11:00 +0100914 if (token_count < 0) {
915 ret = -EINVAL;
Richard Genoudea1b1652020-11-03 12:11:01 +0100916 goto out;
Richard Genoudf2687682020-11-03 12:11:00 +0100917 }
Joao Marcos Costac5100612020-07-30 15:33:47 +0200918
919 path = strdup(filename);
Richard Genoudf2687682020-11-03 12:11:00 +0100920 if (!path) {
921 ret = -EINVAL;
Richard Genoudea1b1652020-11-03 12:11:01 +0100922 goto out;
Richard Genoudf2687682020-11-03 12:11:00 +0100923 }
Joao Marcos Costac5100612020-07-30 15:33:47 +0200924
925 token_list = malloc(token_count * sizeof(char *));
926 if (!token_list) {
927 ret = -EINVAL;
Richard Genoudea1b1652020-11-03 12:11:01 +0100928 goto out;
Joao Marcos Costac5100612020-07-30 15:33:47 +0200929 }
930
931 /* Fill tokens list */
932 ret = sqfs_tokenize(token_list, token_count, path);
933 if (ret)
Richard Genoudea1b1652020-11-03 12:11:01 +0100934 goto out;
Joao Marcos Costac5100612020-07-30 15:33:47 +0200935 /*
936 * ldir's (extended directory) size is greater than dir, so it works as
937 * a general solution for the malloc size, since 'i' is a union.
938 */
939 dirs->inode_table = inode_table;
940 dirs->dir_table = dir_table;
941 ret = sqfs_search_dir(dirs, token_list, token_count, pos_list,
942 metablks_count);
943 if (ret)
Richard Genoudea1b1652020-11-03 12:11:01 +0100944 goto out;
Joao Marcos Costac5100612020-07-30 15:33:47 +0200945
946 if (le16_to_cpu(dirs->i_dir.inode_type) == SQFS_DIR_TYPE)
947 dirs->size = le16_to_cpu(dirs->i_dir.file_size);
948 else
949 dirs->size = le32_to_cpu(dirs->i_ldir.file_size);
950
951 /* Setup directory header */
952 memcpy(dirs->dir_header, dirs->table, SQFS_DIR_HEADER_SIZE);
953 dirs->entry_count = dirs->dir_header->count + 1;
954 dirs->size -= SQFS_DIR_HEADER_SIZE;
955
956 /* Setup entry */
957 dirs->entry = NULL;
958 dirs->table += SQFS_DIR_HEADER_SIZE;
959
960 *dirsp = (struct fs_dir_stream *)dirs;
961
Richard Genoudea1b1652020-11-03 12:11:01 +0100962out:
Joao Marcos Costac5100612020-07-30 15:33:47 +0200963 for (j = 0; j < token_count; j++)
964 free(token_list[j]);
965 free(token_list);
966 free(pos_list);
Joao Marcos Costac5100612020-07-30 15:33:47 +0200967 free(path);
Richard Genoudea1b1652020-11-03 12:11:01 +0100968 if (ret) {
Richard Genoudf2687682020-11-03 12:11:00 +0100969 free(inode_table);
Richard Genoudf2687682020-11-03 12:11:00 +0100970 free(dirs);
Richard Genoudea1b1652020-11-03 12:11:01 +0100971 }
Joao Marcos Costac5100612020-07-30 15:33:47 +0200972
973 return ret;
974}
975
976int sqfs_readdir(struct fs_dir_stream *fs_dirs, struct fs_dirent **dentp)
977{
978 struct squashfs_super_block *sblk = ctxt.sblk;
979 struct squashfs_dir_stream *dirs;
980 struct squashfs_lreg_inode *lreg;
981 struct squashfs_base_inode *base;
982 struct squashfs_reg_inode *reg;
983 int i_number, offset = 0, ret;
984 struct fs_dirent *dent;
985 unsigned char *ipos;
Miquel Raynal2ac0baa2022-06-09 16:02:06 +0200986 u16 name_size;
Joao Marcos Costac5100612020-07-30 15:33:47 +0200987
988 dirs = (struct squashfs_dir_stream *)fs_dirs;
989 if (!dirs->size) {
990 *dentp = NULL;
991 return -SQFS_STOP_READDIR;
992 }
993
994 dent = &dirs->dentp;
995
996 if (!dirs->entry_count) {
997 if (dirs->size > SQFS_DIR_HEADER_SIZE) {
998 dirs->size -= SQFS_DIR_HEADER_SIZE;
999 } else {
1000 *dentp = NULL;
1001 dirs->size = 0;
1002 return -SQFS_STOP_READDIR;
1003 }
1004
1005 if (dirs->size > SQFS_EMPTY_FILE_SIZE) {
1006 /* Read follow-up (emitted) dir. header */
1007 memcpy(dirs->dir_header, dirs->table,
1008 SQFS_DIR_HEADER_SIZE);
1009 dirs->entry_count = dirs->dir_header->count + 1;
1010 ret = sqfs_read_entry(&dirs->entry, dirs->table +
1011 SQFS_DIR_HEADER_SIZE);
1012 if (ret)
1013 return -SQFS_STOP_READDIR;
1014
1015 dirs->table += SQFS_DIR_HEADER_SIZE;
1016 }
1017 } else {
1018 ret = sqfs_read_entry(&dirs->entry, dirs->table);
1019 if (ret)
1020 return -SQFS_STOP_READDIR;
1021 }
1022
1023 i_number = dirs->dir_header->inode_number + dirs->entry->inode_offset;
1024 ipos = sqfs_find_inode(dirs->inode_table, i_number, sblk->inodes,
1025 sblk->block_size);
1026
1027 base = (struct squashfs_base_inode *)ipos;
1028
1029 /* Set entry type and size */
1030 switch (dirs->entry->type) {
1031 case SQFS_DIR_TYPE:
1032 case SQFS_LDIR_TYPE:
1033 dent->type = FS_DT_DIR;
1034 break;
1035 case SQFS_REG_TYPE:
1036 case SQFS_LREG_TYPE:
1037 /*
1038 * Entries do not differentiate extended from regular types, so
1039 * it needs to be verified manually.
1040 */
1041 if (get_unaligned_le16(&base->inode_type) == SQFS_LREG_TYPE) {
1042 lreg = (struct squashfs_lreg_inode *)ipos;
1043 dent->size = get_unaligned_le64(&lreg->file_size);
1044 } else {
1045 reg = (struct squashfs_reg_inode *)ipos;
1046 dent->size = get_unaligned_le32(&reg->file_size);
1047 }
1048
1049 dent->type = FS_DT_REG;
1050 break;
1051 case SQFS_BLKDEV_TYPE:
1052 case SQFS_CHRDEV_TYPE:
1053 case SQFS_LBLKDEV_TYPE:
1054 case SQFS_LCHRDEV_TYPE:
1055 case SQFS_FIFO_TYPE:
1056 case SQFS_SOCKET_TYPE:
1057 case SQFS_LFIFO_TYPE:
1058 case SQFS_LSOCKET_TYPE:
1059 dent->type = SQFS_MISC_ENTRY_TYPE;
1060 break;
1061 case SQFS_SYMLINK_TYPE:
1062 case SQFS_LSYMLINK_TYPE:
1063 dent->type = FS_DT_LNK;
1064 break;
1065 default:
1066 return -SQFS_STOP_READDIR;
1067 }
1068
Miquel Raynal2ac0baa2022-06-09 16:02:06 +02001069 /* Set entry name (capped at FS_DIRENT_NAME_LEN which is a U-Boot limitation) */
1070 name_size = min_t(u16, dirs->entry->name_size + 1, FS_DIRENT_NAME_LEN - 1);
1071 strncpy(dent->name, dirs->entry->name, name_size);
1072 dent->name[name_size] = '\0';
Joao Marcos Costac5100612020-07-30 15:33:47 +02001073
1074 offset = dirs->entry->name_size + 1 + SQFS_ENTRY_BASE_LENGTH;
1075 dirs->entry_count--;
1076
1077 /* Decrement size to be read */
1078 if (dirs->size > offset)
1079 dirs->size -= offset;
1080 else
1081 dirs->size = 0;
1082
1083 /* Keep a reference to the current entry before incrementing it */
1084 dirs->table += offset;
1085
1086 *dentp = dent;
1087
1088 return 0;
1089}
1090
1091int sqfs_probe(struct blk_desc *fs_dev_desc, struct disk_partition *fs_partition)
1092{
1093 struct squashfs_super_block *sblk;
1094 int ret;
1095
1096 ctxt.cur_dev = fs_dev_desc;
1097 ctxt.cur_part_info = *fs_partition;
1098
1099 ret = sqfs_read_sblk(&sblk);
1100 if (ret)
Richard Genoud56cf1ce2020-11-03 12:11:21 +01001101 goto error;
Joao Marcos Costac5100612020-07-30 15:33:47 +02001102
1103 /* Make sure it has a valid SquashFS magic number*/
1104 if (get_unaligned_le32(&sblk->s_magic) != SQFS_MAGIC_NUMBER) {
Simon Glassad6ddc52021-08-18 21:40:27 -06001105 debug("Bad magic number for SquashFS image.\n");
Richard Genoudccd4c082020-11-03 12:11:19 +01001106 ret = -EINVAL;
1107 goto error;
Joao Marcos Costac5100612020-07-30 15:33:47 +02001108 }
1109
1110 ctxt.sblk = sblk;
1111
Joao Marcos Costa10f7cf52020-08-18 17:17:21 +02001112 ret = sqfs_decompressor_init(&ctxt);
Joao Marcos Costa10f7cf52020-08-18 17:17:21 +02001113 if (ret) {
Richard Genoudccd4c082020-11-03 12:11:19 +01001114 goto error;
Joao Marcos Costa10f7cf52020-08-18 17:17:21 +02001115 }
1116
Joao Marcos Costac5100612020-07-30 15:33:47 +02001117 return 0;
Richard Genoudccd4c082020-11-03 12:11:19 +01001118error:
1119 ctxt.cur_dev = NULL;
1120 free(ctxt.sblk);
1121 ctxt.sblk = NULL;
1122 return ret;
Joao Marcos Costac5100612020-07-30 15:33:47 +02001123}
1124
1125static char *sqfs_basename(char *path)
1126{
1127 char *fname;
1128
1129 fname = path + strlen(path) - 1;
1130 while (fname >= path) {
1131 if (*fname == '/') {
1132 fname++;
1133 break;
1134 }
1135
1136 fname--;
1137 }
1138
1139 return fname;
1140}
1141
1142static char *sqfs_dirname(char *path)
1143{
1144 char *fname;
1145
1146 fname = sqfs_basename(path);
1147 --fname;
1148 *fname = '\0';
1149
1150 return path;
1151}
1152
1153/*
1154 * Takes a path to file and splits it in two parts: the filename itself and the
1155 * directory's path, e.g.:
1156 * path: /path/to/file.txt
1157 * file: file.txt
1158 * dir: /path/to
1159 */
1160static int sqfs_split_path(char **file, char **dir, const char *path)
1161{
1162 char *dirc, *basec, *bname, *dname, *tmp_path;
1163 int ret = 0;
1164
Richard Genoud54874772020-11-03 12:11:03 +01001165 *file = NULL;
1166 *dir = NULL;
1167 dirc = NULL;
1168 basec = NULL;
1169 bname = NULL;
1170 dname = NULL;
1171 tmp_path = NULL;
1172
Joao Marcos Costac5100612020-07-30 15:33:47 +02001173 /* check for first slash in path*/
1174 if (path[0] == '/') {
1175 tmp_path = strdup(path);
Richard Genoud54874772020-11-03 12:11:03 +01001176 if (!tmp_path) {
1177 ret = -ENOMEM;
1178 goto out;
1179 }
Joao Marcos Costac5100612020-07-30 15:33:47 +02001180 } else {
1181 tmp_path = malloc(strlen(path) + 2);
Richard Genoud54874772020-11-03 12:11:03 +01001182 if (!tmp_path) {
1183 ret = -ENOMEM;
1184 goto out;
1185 }
Joao Marcos Costac5100612020-07-30 15:33:47 +02001186 tmp_path[0] = '/';
1187 strcpy(tmp_path + 1, path);
1188 }
1189
1190 /* String duplicates */
1191 dirc = strdup(tmp_path);
1192 if (!dirc) {
1193 ret = -ENOMEM;
Richard Genoud54874772020-11-03 12:11:03 +01001194 goto out;
Joao Marcos Costac5100612020-07-30 15:33:47 +02001195 }
1196
1197 basec = strdup(tmp_path);
1198 if (!basec) {
1199 ret = -ENOMEM;
Richard Genoud54874772020-11-03 12:11:03 +01001200 goto out;
Joao Marcos Costac5100612020-07-30 15:33:47 +02001201 }
1202
1203 dname = sqfs_dirname(dirc);
1204 bname = sqfs_basename(basec);
1205
1206 *file = strdup(bname);
1207
1208 if (!*file) {
1209 ret = -ENOMEM;
Richard Genoud54874772020-11-03 12:11:03 +01001210 goto out;
Joao Marcos Costac5100612020-07-30 15:33:47 +02001211 }
1212
1213 if (*dname == '\0') {
1214 *dir = malloc(2);
1215 if (!*dir) {
1216 ret = -ENOMEM;
Richard Genoud54874772020-11-03 12:11:03 +01001217 goto out;
Joao Marcos Costac5100612020-07-30 15:33:47 +02001218 }
1219
1220 (*dir)[0] = '/';
1221 (*dir)[1] = '\0';
1222 } else {
1223 *dir = strdup(dname);
1224 if (!*dir) {
1225 ret = -ENOMEM;
Richard Genoud54874772020-11-03 12:11:03 +01001226 goto out;
Joao Marcos Costac5100612020-07-30 15:33:47 +02001227 }
1228 }
1229
Richard Genoud54874772020-11-03 12:11:03 +01001230out:
1231 if (ret) {
1232 free(*file);
1233 free(*dir);
1234 *dir = NULL;
1235 *file = NULL;
1236 }
Joao Marcos Costac5100612020-07-30 15:33:47 +02001237 free(basec);
Joao Marcos Costac5100612020-07-30 15:33:47 +02001238 free(dirc);
Joao Marcos Costac5100612020-07-30 15:33:47 +02001239 free(tmp_path);
1240
1241 return ret;
1242}
1243
1244static int sqfs_get_regfile_info(struct squashfs_reg_inode *reg,
1245 struct squashfs_file_info *finfo,
1246 struct squashfs_fragment_block_entry *fentry,
1247 __le32 blksz)
1248{
1249 int datablk_count = 0, ret;
1250
1251 finfo->size = get_unaligned_le32(&reg->file_size);
1252 finfo->offset = get_unaligned_le32(&reg->offset);
1253 finfo->start = get_unaligned_le32(&reg->start_block);
1254 finfo->frag = SQFS_IS_FRAGMENTED(get_unaligned_le32(&reg->fragment));
1255
Joao Marcos Costaa7dc37d2020-09-11 12:21:06 +02001256 if (finfo->frag && finfo->offset == 0xFFFFFFFF)
1257 return -EINVAL;
1258
1259 if (finfo->size < 1 || finfo->start == 0xFFFFFFFF)
Joao Marcos Costac9875a52020-08-19 18:28:41 +02001260 return -EINVAL;
1261
Joao Marcos Costac5100612020-07-30 15:33:47 +02001262 if (finfo->frag) {
1263 datablk_count = finfo->size / le32_to_cpu(blksz);
1264 ret = sqfs_frag_lookup(get_unaligned_le32(&reg->fragment),
1265 fentry);
1266 if (ret < 0)
1267 return -EINVAL;
Joao Marcos Costa0008d802021-05-17 18:20:38 -03001268 finfo->comp = ret;
Joao Marcos Costaa7dc37d2020-09-11 12:21:06 +02001269 if (fentry->size < 1 || fentry->start == 0x7FFFFFFF)
Joao Marcos Costac9875a52020-08-19 18:28:41 +02001270 return -EINVAL;
Joao Marcos Costac5100612020-07-30 15:33:47 +02001271 } else {
1272 datablk_count = DIV_ROUND_UP(finfo->size, le32_to_cpu(blksz));
1273 }
1274
1275 finfo->blk_sizes = malloc(datablk_count * sizeof(u32));
1276 if (!finfo->blk_sizes)
1277 return -ENOMEM;
1278
1279 return datablk_count;
1280}
1281
1282static int sqfs_get_lregfile_info(struct squashfs_lreg_inode *lreg,
1283 struct squashfs_file_info *finfo,
1284 struct squashfs_fragment_block_entry *fentry,
1285 __le32 blksz)
1286{
1287 int datablk_count = 0, ret;
1288
1289 finfo->size = get_unaligned_le64(&lreg->file_size);
1290 finfo->offset = get_unaligned_le32(&lreg->offset);
1291 finfo->start = get_unaligned_le64(&lreg->start_block);
1292 finfo->frag = SQFS_IS_FRAGMENTED(get_unaligned_le32(&lreg->fragment));
1293
Joao Marcos Costaa7dc37d2020-09-11 12:21:06 +02001294 if (finfo->frag && finfo->offset == 0xFFFFFFFF)
1295 return -EINVAL;
1296
1297 if (finfo->size < 1 || finfo->start == 0x7FFFFFFF)
Joao Marcos Costac9875a52020-08-19 18:28:41 +02001298 return -EINVAL;
1299
Joao Marcos Costac5100612020-07-30 15:33:47 +02001300 if (finfo->frag) {
1301 datablk_count = finfo->size / le32_to_cpu(blksz);
1302 ret = sqfs_frag_lookup(get_unaligned_le32(&lreg->fragment),
1303 fentry);
1304 if (ret < 0)
1305 return -EINVAL;
Joao Marcos Costa0008d802021-05-17 18:20:38 -03001306 finfo->comp = ret;
Joao Marcos Costaa7dc37d2020-09-11 12:21:06 +02001307 if (fentry->size < 1 || fentry->start == 0x7FFFFFFF)
Joao Marcos Costac9875a52020-08-19 18:28:41 +02001308 return -EINVAL;
Joao Marcos Costac5100612020-07-30 15:33:47 +02001309 } else {
1310 datablk_count = DIV_ROUND_UP(finfo->size, le32_to_cpu(blksz));
1311 }
1312
1313 finfo->blk_sizes = malloc(datablk_count * sizeof(u32));
1314 if (!finfo->blk_sizes)
1315 return -ENOMEM;
1316
1317 return datablk_count;
1318}
1319
1320int sqfs_read(const char *filename, void *buf, loff_t offset, loff_t len,
1321 loff_t *actread)
1322{
Heinrich Schuchardt9bd89bb2022-04-11 22:54:44 +02001323 char *dir = NULL, *fragment_block, *datablock = NULL;
Richard Genoud571b67e2020-11-03 12:11:18 +01001324 char *fragment = NULL, *file = NULL, *resolved, *data;
Campbell Suter9dba07f2020-11-23 15:40:03 +13001325 u64 start, n_blks, table_size, data_offset, table_offset, sparse_size;
Joao Marcos Costacdc11442020-08-18 17:17:22 +02001326 int ret, j, i_number, datablk_count = 0;
Joao Marcos Costac5100612020-07-30 15:33:47 +02001327 struct squashfs_super_block *sblk = ctxt.sblk;
1328 struct squashfs_fragment_block_entry frag_entry;
1329 struct squashfs_file_info finfo = {0};
1330 struct squashfs_symlink_inode *symlink;
1331 struct fs_dir_stream *dirsp = NULL;
1332 struct squashfs_dir_stream *dirs;
1333 struct squashfs_lreg_inode *lreg;
1334 struct squashfs_base_inode *base;
1335 struct squashfs_reg_inode *reg;
1336 unsigned long dest_len;
1337 struct fs_dirent *dent;
1338 unsigned char *ipos;
1339
1340 *actread = 0;
1341
Richard Genoud21b1b3b2020-11-03 12:11:24 +01001342 if (offset) {
1343 /*
1344 * TODO: implement reading at an offset in file
1345 */
1346 printf("Error: reading at a specific offset in a squashfs file is not supported yet.\n");
1347 return -EINVAL;
1348 }
1349
Joao Marcos Costac5100612020-07-30 15:33:47 +02001350 /*
1351 * sqfs_opendir will uncompress inode and directory tables, and will
1352 * return a pointer to the directory that contains the requested file.
1353 */
1354 sqfs_split_path(&file, &dir, filename);
1355 ret = sqfs_opendir(dir, &dirsp);
1356 if (ret) {
Richard Genoud571b67e2020-11-03 12:11:18 +01001357 goto out;
Joao Marcos Costac5100612020-07-30 15:33:47 +02001358 }
1359
1360 dirs = (struct squashfs_dir_stream *)dirsp;
1361
1362 /* For now, only regular files are able to be loaded */
1363 while (!sqfs_readdir(dirsp, &dent)) {
1364 ret = strcmp(dent->name, file);
1365 if (!ret)
1366 break;
1367
1368 free(dirs->entry);
Richard Genoudd1d8d752020-11-03 12:11:11 +01001369 dirs->entry = NULL;
Joao Marcos Costac5100612020-07-30 15:33:47 +02001370 }
1371
1372 if (ret) {
1373 printf("File not found.\n");
1374 *actread = 0;
Joao Marcos Costac5100612020-07-30 15:33:47 +02001375 ret = -ENOENT;
Richard Genoud571b67e2020-11-03 12:11:18 +01001376 goto out;
Joao Marcos Costac5100612020-07-30 15:33:47 +02001377 }
1378
1379 i_number = dirs->dir_header->inode_number + dirs->entry->inode_offset;
1380 ipos = sqfs_find_inode(dirs->inode_table, i_number, sblk->inodes,
1381 sblk->block_size);
1382
1383 base = (struct squashfs_base_inode *)ipos;
1384 switch (get_unaligned_le16(&base->inode_type)) {
1385 case SQFS_REG_TYPE:
1386 reg = (struct squashfs_reg_inode *)ipos;
1387 datablk_count = sqfs_get_regfile_info(reg, &finfo, &frag_entry,
1388 sblk->block_size);
1389 if (datablk_count < 0) {
1390 ret = -EINVAL;
Richard Genoud571b67e2020-11-03 12:11:18 +01001391 goto out;
Joao Marcos Costac5100612020-07-30 15:33:47 +02001392 }
1393
1394 memcpy(finfo.blk_sizes, ipos + sizeof(*reg),
1395 datablk_count * sizeof(u32));
1396 break;
1397 case SQFS_LREG_TYPE:
1398 lreg = (struct squashfs_lreg_inode *)ipos;
1399 datablk_count = sqfs_get_lregfile_info(lreg, &finfo,
1400 &frag_entry,
1401 sblk->block_size);
1402 if (datablk_count < 0) {
1403 ret = -EINVAL;
Richard Genoud571b67e2020-11-03 12:11:18 +01001404 goto out;
Joao Marcos Costac5100612020-07-30 15:33:47 +02001405 }
1406
1407 memcpy(finfo.blk_sizes, ipos + sizeof(*lreg),
1408 datablk_count * sizeof(u32));
1409 break;
1410 case SQFS_SYMLINK_TYPE:
1411 case SQFS_LSYMLINK_TYPE:
1412 symlink = (struct squashfs_symlink_inode *)ipos;
1413 resolved = sqfs_resolve_symlink(symlink, filename);
1414 ret = sqfs_read(resolved, buf, offset, len, actread);
1415 free(resolved);
Richard Genoud571b67e2020-11-03 12:11:18 +01001416 goto out;
Joao Marcos Costac5100612020-07-30 15:33:47 +02001417 case SQFS_BLKDEV_TYPE:
1418 case SQFS_CHRDEV_TYPE:
1419 case SQFS_LBLKDEV_TYPE:
1420 case SQFS_LCHRDEV_TYPE:
1421 case SQFS_FIFO_TYPE:
1422 case SQFS_SOCKET_TYPE:
1423 case SQFS_LFIFO_TYPE:
1424 case SQFS_LSOCKET_TYPE:
1425 default:
1426 printf("Unsupported entry type\n");
1427 ret = -EINVAL;
Richard Genoud571b67e2020-11-03 12:11:18 +01001428 goto out;
Joao Marcos Costac5100612020-07-30 15:33:47 +02001429 }
1430
1431 /* If the user specifies a length, check its sanity */
1432 if (len) {
1433 if (len > finfo.size) {
1434 ret = -EINVAL;
Richard Genoud571b67e2020-11-03 12:11:18 +01001435 goto out;
Joao Marcos Costac5100612020-07-30 15:33:47 +02001436 }
1437
1438 finfo.size = len;
Richard Genoudcbd5e402020-11-03 12:11:23 +01001439 } else {
1440 len = finfo.size;
Joao Marcos Costac5100612020-07-30 15:33:47 +02001441 }
1442
1443 if (datablk_count) {
1444 data_offset = finfo.start;
1445 datablock = malloc(get_unaligned_le32(&sblk->block_size));
1446 if (!datablock) {
1447 ret = -ENOMEM;
Richard Genoud571b67e2020-11-03 12:11:18 +01001448 goto out;
Joao Marcos Costac5100612020-07-30 15:33:47 +02001449 }
1450 }
1451
1452 for (j = 0; j < datablk_count; j++) {
Heinrich Schuchardt9bd89bb2022-04-11 22:54:44 +02001453 char *data_buffer;
1454
Sean Nyekjaer92080c62022-05-12 20:37:14 +02001455 start = lldiv(data_offset, ctxt.cur_dev->blksz);
Joao Marcos Costac5100612020-07-30 15:33:47 +02001456 table_size = SQFS_BLOCK_SIZE(finfo.blk_sizes[j]);
1457 table_offset = data_offset - (start * ctxt.cur_dev->blksz);
1458 n_blks = DIV_ROUND_UP(table_size + table_offset,
1459 ctxt.cur_dev->blksz);
1460
Campbell Suter9dba07f2020-11-23 15:40:03 +13001461 /* Don't load any data for sparse blocks */
1462 if (finfo.blk_sizes[j] == 0) {
1463 n_blks = 0;
1464 table_offset = 0;
1465 data_buffer = NULL;
1466 data = NULL;
1467 } else {
1468 data_buffer = malloc_cache_aligned(n_blks * ctxt.cur_dev->blksz);
Joao Marcos Costac5100612020-07-30 15:33:47 +02001469
Campbell Suter9dba07f2020-11-23 15:40:03 +13001470 if (!data_buffer) {
1471 ret = -ENOMEM;
1472 goto out;
1473 }
1474
1475 ret = sqfs_disk_read(start, n_blks, data_buffer);
1476 if (ret < 0) {
1477 /*
1478 * Possible causes: too many data blocks or too large
1479 * SquashFS block size. Tip: re-compile the SquashFS
1480 * image with mksquashfs's -b <block_size> option.
1481 */
1482 printf("Error: too many data blocks to be read.\n");
1483 goto out;
1484 }
1485
1486 data = data_buffer + table_offset;
Joao Marcos Costac5100612020-07-30 15:33:47 +02001487 }
1488
Joao Marcos Costac5100612020-07-30 15:33:47 +02001489 /* Load the data */
Campbell Suter9dba07f2020-11-23 15:40:03 +13001490 if (finfo.blk_sizes[j] == 0) {
1491 /* This is a sparse block */
1492 sparse_size = get_unaligned_le32(&sblk->block_size);
1493 if ((*actread + sparse_size) > len)
1494 sparse_size = len - *actread;
1495 memset(buf + *actread, 0, sparse_size);
1496 *actread += sparse_size;
1497 } else if (SQFS_COMPRESSED_BLOCK(finfo.blk_sizes[j])) {
Joao Marcos Costac5100612020-07-30 15:33:47 +02001498 dest_len = get_unaligned_le32(&sblk->block_size);
Joao Marcos Costacdc11442020-08-18 17:17:22 +02001499 ret = sqfs_decompress(&ctxt, datablock, &dest_len,
Joao Marcos Costac5100612020-07-30 15:33:47 +02001500 data, table_size);
1501 if (ret)
Richard Genoud571b67e2020-11-03 12:11:18 +01001502 goto out;
Joao Marcos Costac5100612020-07-30 15:33:47 +02001503
Richard Genoudcbd5e402020-11-03 12:11:23 +01001504 if ((*actread + dest_len) > len)
1505 dest_len = len - *actread;
Richard Genoud21b1b3b2020-11-03 12:11:24 +01001506 memcpy(buf + *actread, datablock, dest_len);
Joao Marcos Costac5100612020-07-30 15:33:47 +02001507 *actread += dest_len;
1508 } else {
Richard Genoudcbd5e402020-11-03 12:11:23 +01001509 if ((*actread + table_size) > len)
1510 table_size = len - *actread;
Richard Genoud21b1b3b2020-11-03 12:11:24 +01001511 memcpy(buf + *actread, data, table_size);
Joao Marcos Costac5100612020-07-30 15:33:47 +02001512 *actread += table_size;
1513 }
1514
1515 data_offset += table_size;
Heinrich Schuchardt9bd89bb2022-04-11 22:54:44 +02001516 free(data_buffer);
Richard Genoudcbd5e402020-11-03 12:11:23 +01001517 if (*actread >= len)
1518 break;
Joao Marcos Costac5100612020-07-30 15:33:47 +02001519 }
1520
Joao Marcos Costac5100612020-07-30 15:33:47 +02001521 /*
1522 * There is no need to continue if the file is not fragmented.
1523 */
1524 if (!finfo.frag) {
1525 ret = 0;
Richard Genoud571b67e2020-11-03 12:11:18 +01001526 goto out;
Joao Marcos Costac5100612020-07-30 15:33:47 +02001527 }
1528
Sean Nyekjaer92080c62022-05-12 20:37:14 +02001529 start = lldiv(frag_entry.start, ctxt.cur_dev->blksz);
Joao Marcos Costac5100612020-07-30 15:33:47 +02001530 table_size = SQFS_BLOCK_SIZE(frag_entry.size);
1531 table_offset = frag_entry.start - (start * ctxt.cur_dev->blksz);
1532 n_blks = DIV_ROUND_UP(table_size + table_offset, ctxt.cur_dev->blksz);
1533
1534 fragment = malloc_cache_aligned(n_blks * ctxt.cur_dev->blksz);
1535
1536 if (!fragment) {
1537 ret = -ENOMEM;
Richard Genoud571b67e2020-11-03 12:11:18 +01001538 goto out;
Joao Marcos Costac5100612020-07-30 15:33:47 +02001539 }
1540
1541 ret = sqfs_disk_read(start, n_blks, fragment);
1542 if (ret < 0)
Richard Genoud571b67e2020-11-03 12:11:18 +01001543 goto out;
Joao Marcos Costac5100612020-07-30 15:33:47 +02001544
1545 /* File compressed and fragmented */
1546 if (finfo.frag && finfo.comp) {
1547 dest_len = get_unaligned_le32(&sblk->block_size);
1548 fragment_block = malloc(dest_len);
1549 if (!fragment_block) {
1550 ret = -ENOMEM;
Richard Genoud571b67e2020-11-03 12:11:18 +01001551 goto out;
Joao Marcos Costac5100612020-07-30 15:33:47 +02001552 }
1553
Joao Marcos Costacdc11442020-08-18 17:17:22 +02001554 ret = sqfs_decompress(&ctxt, fragment_block, &dest_len,
Joao Marcos Costac5100612020-07-30 15:33:47 +02001555 (void *)fragment + table_offset,
1556 frag_entry.size);
1557 if (ret) {
1558 free(fragment_block);
Richard Genoud571b67e2020-11-03 12:11:18 +01001559 goto out;
Joao Marcos Costac5100612020-07-30 15:33:47 +02001560 }
1561
Joao Marcos Costa0008d802021-05-17 18:20:38 -03001562 memcpy(buf + *actread, &fragment_block[finfo.offset], finfo.size - *actread);
1563 *actread = finfo.size;
Joao Marcos Costac5100612020-07-30 15:33:47 +02001564
1565 free(fragment_block);
1566
1567 } else if (finfo.frag && !finfo.comp) {
1568 fragment_block = (void *)fragment + table_offset;
1569
Joao Marcos Costa0008d802021-05-17 18:20:38 -03001570 memcpy(buf + *actread, &fragment_block[finfo.offset], finfo.size - *actread);
1571 *actread = finfo.size;
Joao Marcos Costac5100612020-07-30 15:33:47 +02001572 }
1573
Richard Genoud571b67e2020-11-03 12:11:18 +01001574out:
Joao Marcos Costac5100612020-07-30 15:33:47 +02001575 free(fragment);
Heinrich Schuchardt9bd89bb2022-04-11 22:54:44 +02001576 free(datablock);
Joao Marcos Costac5100612020-07-30 15:33:47 +02001577 free(file);
1578 free(dir);
Richard Genoud571b67e2020-11-03 12:11:18 +01001579 free(finfo.blk_sizes);
Richard Genoud7ce97452020-11-03 12:11:13 +01001580 sqfs_closedir(dirsp);
Joao Marcos Costac5100612020-07-30 15:33:47 +02001581
1582 return ret;
1583}
1584
1585int sqfs_size(const char *filename, loff_t *size)
1586{
1587 struct squashfs_super_block *sblk = ctxt.sblk;
1588 struct squashfs_symlink_inode *symlink;
1589 struct fs_dir_stream *dirsp = NULL;
1590 struct squashfs_base_inode *base;
1591 struct squashfs_dir_stream *dirs;
1592 struct squashfs_lreg_inode *lreg;
1593 struct squashfs_reg_inode *reg;
1594 char *dir, *file, *resolved;
1595 struct fs_dirent *dent;
1596 unsigned char *ipos;
1597 int ret, i_number;
1598
1599 sqfs_split_path(&file, &dir, filename);
1600 /*
1601 * sqfs_opendir will uncompress inode and directory tables, and will
1602 * return a pointer to the directory that contains the requested file.
1603 */
1604 ret = sqfs_opendir(dir, &dirsp);
1605 if (ret) {
Joao Marcos Costac5100612020-07-30 15:33:47 +02001606 ret = -EINVAL;
1607 goto free_strings;
1608 }
1609
1610 dirs = (struct squashfs_dir_stream *)dirsp;
1611
1612 while (!sqfs_readdir(dirsp, &dent)) {
1613 ret = strcmp(dent->name, file);
1614 if (!ret)
1615 break;
1616 free(dirs->entry);
Richard Genoud508a9dc2020-11-03 12:11:09 +01001617 dirs->entry = NULL;
Joao Marcos Costac5100612020-07-30 15:33:47 +02001618 }
1619
1620 if (ret) {
1621 printf("File not found.\n");
1622 *size = 0;
1623 ret = -EINVAL;
1624 goto free_strings;
1625 }
1626
1627 i_number = dirs->dir_header->inode_number + dirs->entry->inode_offset;
1628 ipos = sqfs_find_inode(dirs->inode_table, i_number, sblk->inodes,
1629 sblk->block_size);
1630 free(dirs->entry);
Richard Genoud508a9dc2020-11-03 12:11:09 +01001631 dirs->entry = NULL;
Joao Marcos Costac5100612020-07-30 15:33:47 +02001632
1633 base = (struct squashfs_base_inode *)ipos;
1634 switch (get_unaligned_le16(&base->inode_type)) {
1635 case SQFS_REG_TYPE:
1636 reg = (struct squashfs_reg_inode *)ipos;
1637 *size = get_unaligned_le32(&reg->file_size);
1638 break;
1639 case SQFS_LREG_TYPE:
1640 lreg = (struct squashfs_lreg_inode *)ipos;
1641 *size = get_unaligned_le64(&lreg->file_size);
1642 break;
1643 case SQFS_SYMLINK_TYPE:
1644 case SQFS_LSYMLINK_TYPE:
1645 symlink = (struct squashfs_symlink_inode *)ipos;
1646 resolved = sqfs_resolve_symlink(symlink, filename);
1647 ret = sqfs_size(resolved, size);
1648 free(resolved);
1649 break;
1650 case SQFS_BLKDEV_TYPE:
1651 case SQFS_CHRDEV_TYPE:
1652 case SQFS_LBLKDEV_TYPE:
1653 case SQFS_LCHRDEV_TYPE:
1654 case SQFS_FIFO_TYPE:
1655 case SQFS_SOCKET_TYPE:
1656 case SQFS_LFIFO_TYPE:
1657 case SQFS_LSOCKET_TYPE:
1658 default:
1659 printf("Unable to recover entry's size.\n");
1660 *size = 0;
1661 ret = -EINVAL;
1662 break;
1663 }
1664
1665free_strings:
1666 free(dir);
1667 free(file);
1668
1669 sqfs_closedir(dirsp);
1670
1671 return ret;
1672}
1673
Richard Genouddd4866b2020-11-03 12:11:26 +01001674int sqfs_exists(const char *filename)
1675{
1676 struct fs_dir_stream *dirsp = NULL;
1677 struct squashfs_dir_stream *dirs;
1678 char *dir, *file;
1679 struct fs_dirent *dent;
1680 int ret;
1681
1682 sqfs_split_path(&file, &dir, filename);
1683 /*
1684 * sqfs_opendir will uncompress inode and directory tables, and will
1685 * return a pointer to the directory that contains the requested file.
1686 */
1687 ret = sqfs_opendir(dir, &dirsp);
1688 if (ret) {
1689 ret = -EINVAL;
1690 goto free_strings;
1691 }
1692
1693 dirs = (struct squashfs_dir_stream *)dirsp;
1694
1695 while (!sqfs_readdir(dirsp, &dent)) {
1696 ret = strcmp(dent->name, file);
1697 if (!ret)
1698 break;
1699 free(dirs->entry);
1700 dirs->entry = NULL;
1701 }
1702
1703 sqfs_closedir(dirsp);
1704
1705free_strings:
1706 free(dir);
1707 free(file);
1708
1709 return ret == 0;
1710}
1711
Joao Marcos Costac5100612020-07-30 15:33:47 +02001712void sqfs_close(void)
1713{
Joao Marcos Costa10f7cf52020-08-18 17:17:21 +02001714 sqfs_decompressor_cleanup(&ctxt);
Richard Genoud7e932ac2020-11-24 18:07:52 +01001715 free(ctxt.sblk);
1716 ctxt.sblk = NULL;
1717 ctxt.cur_dev = NULL;
Joao Marcos Costac5100612020-07-30 15:33:47 +02001718}
1719
1720void sqfs_closedir(struct fs_dir_stream *dirs)
1721{
1722 struct squashfs_dir_stream *sqfs_dirs;
1723
Heinrich Schuchardt220fa472021-02-01 03:28:48 +01001724 if (!dirs)
1725 return;
1726
Joao Marcos Costac5100612020-07-30 15:33:47 +02001727 sqfs_dirs = (struct squashfs_dir_stream *)dirs;
1728 free(sqfs_dirs->inode_table);
1729 free(sqfs_dirs->dir_table);
1730 free(sqfs_dirs->dir_header);
Richard Genoud87d11e02020-11-03 12:11:02 +01001731 free(sqfs_dirs);
Joao Marcos Costac5100612020-07-30 15:33:47 +02001732}