mirror of https://github.com/ventoy/Ventoy
429 lines
11 KiB
C
429 lines
11 KiB
C
/*
|
|
* Read a squashfs filesystem. This is a highly compressed read only
|
|
* filesystem.
|
|
*
|
|
* Copyright (c) 2010, 2012, 2013, 2019
|
|
* Phillip Lougher <phillip@squashfs.org.uk>
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2,
|
|
* or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
*
|
|
* read_xattrs.c
|
|
*/
|
|
|
|
/*
|
|
* Common xattr read code shared between mksquashfs and unsquashfs
|
|
*/
|
|
|
|
#define TRUE 1
|
|
#define FALSE 0
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#ifndef linux
|
|
#define __BYTE_ORDER BYTE_ORDER
|
|
#define __BIG_ENDIAN BIG_ENDIAN
|
|
#define __LITTLE_ENDIAN LITTLE_ENDIAN
|
|
#else
|
|
#include <endian.h>
|
|
#endif
|
|
|
|
#include "squashfs_fs.h"
|
|
#include "squashfs_swap.h"
|
|
#include "xattr.h"
|
|
#include "error.h"
|
|
|
|
#include <stdlib.h>
|
|
|
|
extern int read_fs_bytes(int, long long, int, void *);
|
|
extern int read_block(int, long long, long long *, int, void *);
|
|
|
|
static struct hash_entry {
|
|
long long start;
|
|
unsigned int offset;
|
|
struct hash_entry *next;
|
|
} *hash_table[65536];
|
|
|
|
static struct squashfs_xattr_id *xattr_ids;
|
|
static void *xattrs = NULL;
|
|
static long long xattr_table_start;
|
|
|
|
/*
|
|
* Prefix lookup table, storing mapping to/from prefix string and prefix id
|
|
*/
|
|
struct prefix prefix_table[] = {
|
|
{ "user.", SQUASHFS_XATTR_USER },
|
|
{ "trusted.", SQUASHFS_XATTR_TRUSTED },
|
|
{ "security.", SQUASHFS_XATTR_SECURITY },
|
|
{ "", -1 }
|
|
};
|
|
|
|
/*
|
|
* store mapping from location of compressed block in fs ->
|
|
* location of uncompressed block in memory
|
|
*/
|
|
static void save_xattr_block(long long start, int offset)
|
|
{
|
|
struct hash_entry *hash_entry = malloc(sizeof(*hash_entry));
|
|
int hash = start & 0xffff;
|
|
|
|
TRACE("save_xattr_block: start %lld, offset %d\n", start, offset);
|
|
|
|
if(hash_entry == NULL)
|
|
MEM_ERROR();
|
|
|
|
hash_entry->start = start;
|
|
hash_entry->offset = offset;
|
|
hash_entry->next = hash_table[hash];
|
|
hash_table[hash] = hash_entry;
|
|
}
|
|
|
|
|
|
/*
|
|
* map from location of compressed block in fs ->
|
|
* location of uncompressed block in memory
|
|
*/
|
|
static int get_xattr_block(long long start)
|
|
{
|
|
int hash = start & 0xffff;
|
|
struct hash_entry *hash_entry = hash_table[hash];
|
|
|
|
for(; hash_entry; hash_entry = hash_entry->next)
|
|
if(hash_entry->start == start)
|
|
break;
|
|
|
|
TRACE("get_xattr_block: start %lld, offset %d\n", start,
|
|
hash_entry ? hash_entry->offset : -1);
|
|
|
|
return hash_entry ? hash_entry->offset : -1;
|
|
}
|
|
|
|
|
|
/*
|
|
* construct the xattr_list entry from the fs xattr, including
|
|
* mapping name and prefix into a full name
|
|
*/
|
|
static int read_xattr_entry(struct xattr_list *xattr,
|
|
struct squashfs_xattr_entry *entry, void *name)
|
|
{
|
|
int i, len, type = entry->type & XATTR_PREFIX_MASK;
|
|
|
|
for(i = 0; prefix_table[i].type != -1; i++)
|
|
if(prefix_table[i].type == type)
|
|
break;
|
|
|
|
if(prefix_table[i].type == -1) {
|
|
ERROR("read_xattr_entry: Unrecognised xattr type %d\n", type);
|
|
return 0;
|
|
}
|
|
|
|
len = strlen(prefix_table[i].prefix);
|
|
xattr->full_name = malloc(len + entry->size + 1);
|
|
if(xattr->full_name == NULL)
|
|
MEM_ERROR();
|
|
|
|
memcpy(xattr->full_name, prefix_table[i].prefix, len);
|
|
memcpy(xattr->full_name + len, name, entry->size);
|
|
xattr->full_name[len + entry->size] = '\0';
|
|
xattr->name = xattr->full_name + len;
|
|
xattr->size = entry->size;
|
|
xattr->type = type;
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
/*
|
|
* Read and decompress the xattr id table and the xattr metadata.
|
|
* This is cached in memory for later use by get_xattr()
|
|
*/
|
|
int read_xattrs_from_disk(int fd, struct squashfs_super_block *sBlk, int flag, long long *table_start)
|
|
{
|
|
/*
|
|
* Note on overflow limits:
|
|
* Size of ids (id_table.xattr_ids) is 2^32 (unsigned int)
|
|
* Max size of bytes is 2^32*16 or 2^36
|
|
* Max indexes is (2^32*16)/8K or 2^23
|
|
* Max index_bytes is ((2^32*16)/8K)*8 or 2^26 or 64M
|
|
*/
|
|
int res, i, indexes, index_bytes;
|
|
unsigned int ids;
|
|
long long bytes;
|
|
long long *index, start, end;
|
|
struct squashfs_xattr_table id_table;
|
|
|
|
TRACE("read_xattrs_from_disk\n");
|
|
|
|
if(sBlk->xattr_id_table_start == SQUASHFS_INVALID_BLK)
|
|
return SQUASHFS_INVALID_BLK;
|
|
|
|
/*
|
|
* Read xattr id table, containing start of xattr metadata and the
|
|
* number of xattrs in the file system
|
|
*/
|
|
res = read_fs_bytes(fd, sBlk->xattr_id_table_start, sizeof(id_table),
|
|
&id_table);
|
|
if(res == 0)
|
|
return 0;
|
|
|
|
SQUASHFS_INSWAP_XATTR_TABLE(&id_table);
|
|
|
|
/*
|
|
* Compute index table values
|
|
*/
|
|
ids = id_table.xattr_ids;
|
|
xattr_table_start = id_table.xattr_table_start;
|
|
index_bytes = SQUASHFS_XATTR_BLOCK_BYTES((long long) ids);
|
|
indexes = SQUASHFS_XATTR_BLOCKS((long long) ids);
|
|
|
|
/*
|
|
* The size of the index table (index_bytes) should match the
|
|
* table start and end points
|
|
*/
|
|
if(index_bytes != (sBlk->bytes_used - (sBlk->xattr_id_table_start + sizeof(id_table)))) {
|
|
ERROR("read_xattrs_from_disk: Bad xattr_ids count in super block\n");
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* id_table.xattr_table_start stores the start of the compressed xattr
|
|
* metadata blocks. This by definition is also the end of the previous
|
|
* filesystem table - the id lookup table.
|
|
*/
|
|
if(table_start != NULL)
|
|
*table_start = id_table.xattr_table_start;
|
|
|
|
/*
|
|
* If flag is set then return once we've read the above
|
|
* table_start. That value is necessary for sanity checking,
|
|
* but we don't actually want to extract the xattrs, and so
|
|
* stop here.
|
|
*/
|
|
if(flag)
|
|
return id_table.xattr_ids;
|
|
|
|
/*
|
|
* Allocate and read the index to the xattr id table metadata
|
|
* blocks
|
|
*/
|
|
index = malloc(index_bytes);
|
|
if(index == NULL)
|
|
MEM_ERROR();
|
|
|
|
res = read_fs_bytes(fd, sBlk->xattr_id_table_start + sizeof(id_table),
|
|
index_bytes, index);
|
|
if(res ==0)
|
|
goto failed1;
|
|
|
|
SQUASHFS_INSWAP_LONG_LONGS(index, indexes);
|
|
|
|
/*
|
|
* Allocate enough space for the uncompressed xattr id table, and
|
|
* read and decompress it
|
|
*/
|
|
bytes = SQUASHFS_XATTR_BYTES((long long) ids);
|
|
xattr_ids = malloc(bytes);
|
|
if(xattr_ids == NULL)
|
|
MEM_ERROR();
|
|
|
|
for(i = 0; i < indexes; i++) {
|
|
int expected = (i + 1) != indexes ? SQUASHFS_METADATA_SIZE :
|
|
bytes & (SQUASHFS_METADATA_SIZE - 1);
|
|
int length = read_block(fd, index[i], NULL, expected,
|
|
((unsigned char *) xattr_ids) +
|
|
((long long) i * SQUASHFS_METADATA_SIZE));
|
|
TRACE("Read xattr id table block %d, from 0x%llx, length "
|
|
"%d\n", i, index[i], length);
|
|
if(length == 0) {
|
|
ERROR("Failed to read xattr id table block %d, "
|
|
"from 0x%llx, length %d\n", i, index[i],
|
|
length);
|
|
goto failed2;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Read and decompress the xattr metadata
|
|
*
|
|
* Note the first xattr id table metadata block is immediately after
|
|
* the last xattr metadata block, so we can use index[0] to work out
|
|
* the end of the xattr metadata
|
|
*/
|
|
start = xattr_table_start;
|
|
end = index[0];
|
|
for(i = 0; start < end; i++) {
|
|
int length;
|
|
xattrs = realloc(xattrs, (i + 1) * SQUASHFS_METADATA_SIZE);
|
|
if(xattrs == NULL)
|
|
MEM_ERROR();
|
|
|
|
/* store mapping from location of compressed block in fs ->
|
|
* location of uncompressed block in memory */
|
|
save_xattr_block(start, i * SQUASHFS_METADATA_SIZE);
|
|
|
|
length = read_block(fd, start, &start, 0,
|
|
((unsigned char *) xattrs) +
|
|
(i * SQUASHFS_METADATA_SIZE));
|
|
TRACE("Read xattr block %d, length %d\n", i, length);
|
|
if(length == 0) {
|
|
ERROR("Failed to read xattr block %d\n", i);
|
|
goto failed3;
|
|
}
|
|
|
|
/*
|
|
* If this is not the last metadata block in the xattr metadata
|
|
* then it should be SQUASHFS_METADATA_SIZE in size.
|
|
* Note, we can't use expected in read_block() above for this
|
|
* because we don't know if this is the last block until
|
|
* after reading.
|
|
*/
|
|
if(start != end && length != SQUASHFS_METADATA_SIZE) {
|
|
ERROR("Xattr block %d should be %d bytes in length, "
|
|
"it is %d bytes\n", i, SQUASHFS_METADATA_SIZE,
|
|
length);
|
|
goto failed3;
|
|
}
|
|
}
|
|
|
|
/* swap if necessary the xattr id entries */
|
|
for(i = 0; i < ids; i++)
|
|
SQUASHFS_INSWAP_XATTR_ID(&xattr_ids[i]);
|
|
|
|
free(index);
|
|
|
|
return ids;
|
|
|
|
failed3:
|
|
free(xattrs);
|
|
failed2:
|
|
free(xattr_ids);
|
|
failed1:
|
|
free(index);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
void free_xattr(struct xattr_list *xattr_list, int count)
|
|
{
|
|
int i;
|
|
|
|
for(i = 0; i < count; i++)
|
|
free(xattr_list[i].full_name);
|
|
|
|
free(xattr_list);
|
|
}
|
|
|
|
|
|
/*
|
|
* Construct and return the list of xattr name:value pairs for the passed xattr
|
|
* id
|
|
*
|
|
* There are two users for get_xattr(), Mksquashfs uses it to read the
|
|
* xattrs from the filesystem on appending, and Unsquashfs uses it
|
|
* to retrieve the xattrs for writing to disk.
|
|
*
|
|
* Unfortunately, the two users disagree on what to do with unknown
|
|
* xattr prefixes, Mksquashfs wants to treat this as fatal otherwise
|
|
* this will cause xattrs to be be lost on appending. Unsquashfs
|
|
* on the otherhand wants to retrieve the xattrs which are known and
|
|
* to ignore the rest, this allows Unsquashfs to cope more gracefully
|
|
* with future versions which may have unknown xattrs, as long as the
|
|
* general xattr structure is adhered to, Unsquashfs should be able
|
|
* to safely ignore unknown xattrs, and to write the ones it knows about,
|
|
* this is better than completely refusing to retrieve all the xattrs.
|
|
*
|
|
* So return an error flag if any unrecognised types were found.
|
|
*/
|
|
struct xattr_list *get_xattr(int i, unsigned int *count, int *failed)
|
|
{
|
|
long long start;
|
|
struct xattr_list *xattr_list = NULL;
|
|
unsigned int offset;
|
|
void *xptr;
|
|
int j, n, res = 1;
|
|
|
|
TRACE("get_xattr\n");
|
|
|
|
if(xattr_ids[i].count == 0) {
|
|
ERROR("get_xattr: xattr count unexpectedly 0 - corrupt fs?\n");
|
|
*failed = TRUE;
|
|
*count = 0;
|
|
return NULL;
|
|
} else
|
|
*failed = FALSE;
|
|
|
|
start = SQUASHFS_XATTR_BLK(xattr_ids[i].xattr) + xattr_table_start;
|
|
offset = SQUASHFS_XATTR_OFFSET(xattr_ids[i].xattr);
|
|
xptr = xattrs + get_xattr_block(start) + offset;
|
|
|
|
TRACE("get_xattr: xattr_id %d, count %d, start %lld, offset %d\n", i,
|
|
xattr_ids[i].count, start, offset);
|
|
|
|
for(j = 0, n = 0; n < xattr_ids[i].count; n++) {
|
|
struct squashfs_xattr_entry entry;
|
|
struct squashfs_xattr_val val;
|
|
|
|
if(res != 0) {
|
|
xattr_list = realloc(xattr_list, (j + 1) *
|
|
sizeof(struct xattr_list));
|
|
if(xattr_list == NULL)
|
|
MEM_ERROR();
|
|
}
|
|
|
|
SQUASHFS_SWAP_XATTR_ENTRY(xptr, &entry);
|
|
xptr += sizeof(entry);
|
|
|
|
res = read_xattr_entry(&xattr_list[j], &entry, xptr);
|
|
if(res == 0) {
|
|
/* unknown type, skip, and set error flag */
|
|
xptr += entry.size;
|
|
SQUASHFS_SWAP_XATTR_VAL(xptr, &val);
|
|
xptr += sizeof(val) + val.vsize;
|
|
*failed = TRUE;
|
|
continue;
|
|
}
|
|
|
|
xptr += entry.size;
|
|
|
|
TRACE("get_xattr: xattr %d, type %d, size %d, name %s\n", j,
|
|
entry.type, entry.size, xattr_list[j].full_name);
|
|
|
|
if(entry.type & SQUASHFS_XATTR_VALUE_OOL) {
|
|
long long xattr;
|
|
void *ool_xptr;
|
|
|
|
xptr += sizeof(val);
|
|
SQUASHFS_SWAP_LONG_LONGS(xptr, &xattr, 1);
|
|
xptr += sizeof(xattr);
|
|
start = SQUASHFS_XATTR_BLK(xattr) + xattr_table_start;
|
|
offset = SQUASHFS_XATTR_OFFSET(xattr);
|
|
ool_xptr = xattrs + get_xattr_block(start) + offset;
|
|
SQUASHFS_SWAP_XATTR_VAL(ool_xptr, &val);
|
|
xattr_list[j].value = ool_xptr + sizeof(val);
|
|
} else {
|
|
SQUASHFS_SWAP_XATTR_VAL(xptr, &val);
|
|
xattr_list[j].value = xptr + sizeof(val);
|
|
xptr += sizeof(val) + val.vsize;
|
|
}
|
|
|
|
TRACE("get_xattr: xattr %d, vsize %d\n", j, val.vsize);
|
|
|
|
xattr_list[j++].vsize = val.vsize;
|
|
}
|
|
|
|
*count = j;
|
|
return xattr_list;
|
|
}
|