initial commit

This commit is contained in:
longpanda 2020-04-05 00:07:50 +08:00
parent 2090c6fa97
commit 05a1b863a6
487 changed files with 114253 additions and 0 deletions

View file

@ -0,0 +1,365 @@
###############################################
# Compression build options #
###############################################
#
#
############# Building gzip support ###########
#
# Gzip support is by default enabled, and the compression type default
# (COMP_DEFAULT) is gzip.
#
# If you don't want/need gzip support then comment out the GZIP SUPPORT line
# below, and change COMP_DEFAULT to one of the compression types you have
# selected.
#
# Obviously, you must select at least one of the available gzip, lzma, lzo
# compression types.
#
GZIP_SUPPORT = 1
########### Building XZ support #############
#
# LZMA2 compression.
#
# XZ Utils liblzma (http://tukaani.org/xz/) is supported
#
# Development packages (libraries and header files) should be
# supported by most modern distributions. Please refer to
# your distribution package manager.
#
# To build install the library and uncomment
# the XZ_SUPPORT line below.
#
XZ_SUPPORT = 1
############ Building LZO support ##############
#
# The LZO library (http://www.oberhumer.com/opensource/lzo/) is supported.
#
# Development packages (libraries and header files) should be
# supported by most modern distributions. Please refer to
# your distribution package manager.
#
# To build install the library and uncomment
# the XZ_SUPPORT line below.
#
LZO_SUPPORT = 1
########### Building LZ4 support #############
#
# Yann Collet's LZ4 tools are supported
# LZ4 homepage: http://fastcompression.blogspot.com/p/lz4.html
# LZ4 source repository: http://code.google.com/p/lz4
#
# Development packages (libraries and header files) should be
# supported by most modern distributions. Please refer to
# your distribution package manager.
#
# To build install and uncomment
# the LZ4_SUPPORT line below.
#
LZ4_SUPPORT = 1
########### Building ZSTD support ############
#
# The ZSTD library is supported
# ZSTD homepage: http://zstd.net
# ZSTD source repository: https://github.com/facebook/zstd
#
# Development packages (libraries and header files) should be
# supported by most modern distributions. Please refer to
# your distribution package manager.
#
# To build install the library and uncomment
# the XZ_SUPPORT line below.
#
ZSTD_SUPPORT = 1
######## Specifying default compression ########
#
# The next line specifies which compression algorithm is used by default
# in Mksquashfs. Obviously the compression algorithm must have been
# selected to be built
#
COMP_DEFAULT = gzip
###############################################
# Extended attribute (XATTRs) build options #
###############################################
#
# Building XATTR support for Mksquashfs and Unsquashfs
#
# If your C library or build/target environment doesn't support XATTRs then
# comment out the next line to build Mksquashfs and Unsquashfs without XATTR
# support
XATTR_SUPPORT = 1
# Select whether you wish xattrs to be stored by Mksquashfs and extracted
# by Unsquashfs by default. If selected users can disable xattr support by
# using the -no-xattrs option
#
# If unselected, Mksquashfs/Unsquashfs won't store and extract xattrs by
# default. Users can enable xattrs by using the -xattrs option.
XATTR_DEFAULT = 1
###############################################
# Reproducible Image options #
###############################################
#
# Select whether you wish reproducible builds by default. If selected users
# can disable reproducible builds using the not-reproducible option.
# If not selected, users can enable reproducible builds using the
# -reproducible option
REPRODUCIBLE_DEFAULT = 1
###############################################
# Obsolete options #
###############################################
########### Building LZMA support #############
#
# LZMA1 compression.
#
# LZMA1 compression is obsolete, and the newer and better XZ (LZMA2)
# compression should be used in preference.
#
# Both XZ Utils liblzma (http://tukaani.org/xz/) and LZMA SDK
# (http://www.7-zip.org/sdk.html) are supported
#
# To build using XZ Utils liblzma - install the library and uncomment
# the LZMA_XZ_SUPPORT line below.
#
# To build using the LZMA SDK (4.65 used in development, other versions may
# work) - download and unpack it, uncomment and set LZMA_DIR to unpacked source,
# and uncomment the LZMA_SUPPORT line below.
#
LZMA_XZ_SUPPORT = 1
#LZMA_SUPPORT = 1
#LZMA_DIR = ../../../../LZMA/lzma465
###############################################
# End of BUILD options section #
###############################################
INCLUDEDIR = -I.
INSTALL_DIR = /usr/local/bin
MKSQUASHFS_OBJS = mksquashfs.o read_fs.o action.o swap.o pseudo.o compressor.o \
sort.o progressbar.o read_file.o info.o restore.o process_fragments.o \
caches-queues-lists.o
UNSQUASHFS_OBJS = unsquashfs.o unsquash-1.o unsquash-2.o unsquash-3.o \
unsquash-4.o unsquash-123.o unsquash-34.o swap.o compressor.o unsquashfs_info.o
CFLAGS ?= -Os
CFLAGS += $(EXTRA_CFLAGS) $(INCLUDEDIR) -D_FILE_OFFSET_BITS=64 \
-D_LARGEFILE_SOURCE -D_GNU_SOURCE -DCOMP_DEFAULT=\"$(COMP_DEFAULT)\" \
-Wall
LIBS = -lpthread
ifeq ($(GZIP_SUPPORT),1)
CFLAGS += -DGZIP_SUPPORT
MKSQUASHFS_OBJS += gzip_wrapper.o
UNSQUASHFS_OBJS += gzip_wrapper.o
LIBS += $(VTZLIB)
COMPRESSORS += gzip
endif
ifeq ($(LZMA_SUPPORT),1)
LZMA_OBJS = $(LZMA_DIR)/C/Alloc.o $(LZMA_DIR)/C/LzFind.o \
$(LZMA_DIR)/C/LzmaDec.o $(LZMA_DIR)/C/LzmaEnc.o $(LZMA_DIR)/C/LzmaLib.o
INCLUDEDIR += -I$(LZMA_DIR)/C
CFLAGS += -DLZMA_SUPPORT
MKSQUASHFS_OBJS += lzma_wrapper.o $(LZMA_OBJS)
UNSQUASHFS_OBJS += lzma_wrapper.o $(LZMA_OBJS)
COMPRESSORS += lzma
endif
ifeq ($(LZMA_XZ_SUPPORT),1)
CFLAGS += -DLZMA_SUPPORT
MKSQUASHFS_OBJS += lzma_xz_wrapper.o
UNSQUASHFS_OBJS += lzma_xz_wrapper.o
LIBS +=
COMPRESSORS += lzma
endif
ifeq ($(XZ_SUPPORT),1)
CFLAGS += -DXZ_SUPPORT -I$(LZMA_LIBDIR)/include
MKSQUASHFS_OBJS += xz_wrapper.o
UNSQUASHFS_OBJS += xz_wrapper.o
LIBS += $(LZMA_LIBDIR)/lib/liblzma.a
COMPRESSORS += xz
endif
ifeq ($(LZO_SUPPORT),1)
CFLAGS += -DLZO_SUPPORT -I $(LZO_LIBDIR)/include
MKSQUASHFS_OBJS += lzo_wrapper.o
UNSQUASHFS_OBJS += lzo_wrapper.o
LIBS += $(LZO_LIBDIR)/lib/liblzo2.a
COMPRESSORS += lzo
endif
ifeq ($(LZ4_SUPPORT),1)
CFLAGS += -DLZ4_SUPPORT -I $(LZ4_LIBDIR)/include
MKSQUASHFS_OBJS += lz4_wrapper.o
UNSQUASHFS_OBJS += lz4_wrapper.o
LIBS += $(LZ4_LIBDIR)/lib/liblz4.a
COMPRESSORS += lz4
endif
ifeq ($(ZSTD_SUPPORT),1)
CFLAGS += -DZSTD_SUPPORT -I $(ZSTD_LIBDIR)/include
MKSQUASHFS_OBJS += zstd_wrapper.o
UNSQUASHFS_OBJS += zstd_wrapper.o
LIBS += $(ZSTD_LIBDIR)/lib/libzstd.a
COMPRESSORS += zstd
endif
ifeq ($(XATTR_SUPPORT),1)
ifeq ($(XATTR_DEFAULT),1)
CFLAGS += -DXATTR_SUPPORT -DXATTR_DEFAULT
else
CFLAGS += -DXATTR_SUPPORT
endif
MKSQUASHFS_OBJS += xattr.o read_xattrs.o
UNSQUASHFS_OBJS += read_xattrs.o unsquashfs_xattr.o
endif
ifeq ($(REPRODUCIBLE_DEFAULT),1)
CFLAGS += -DREPRODUCIBLE_DEFAULT
endif
#
# If LZMA_SUPPORT is specified then LZMA_DIR must be specified too
#
ifeq ($(LZMA_SUPPORT),1)
ifndef LZMA_DIR
$(error "LZMA_SUPPORT requires LZMA_DIR to be also defined")
endif
endif
#
# Both LZMA_XZ_SUPPORT and LZMA_SUPPORT cannot be specified
#
ifeq ($(LZMA_XZ_SUPPORT),1)
ifeq ($(LZMA_SUPPORT),1)
$(error "Both LZMA_XZ_SUPPORT and LZMA_SUPPORT cannot be specified")
endif
endif
#
# At least one compressor must have been selected
#
ifndef COMPRESSORS
$(error "No compressor selected! Select one or more of GZIP, LZMA, XZ, LZO, \
LZ4 or ZSTD!")
endif
#
# COMP_DEFAULT should be defined
#
ifndef COMP_DEFAULT
$(error "COMP_DEFAULT must be set to a compressor!")
endif
#
# COMP_DEFAULT must be a selected compressor
#
ifeq (, $(findstring $(COMP_DEFAULT), $(COMPRESSORS)))
$(error "COMP_DEFAULT is set to ${COMP_DEFAULT}, which isn't selected to be \
built!")
endif
.PHONY: all
all: mksquashfs unsquashfs
mksquashfs: $(MKSQUASHFS_OBJS)
$(CC) $(LDFLAGS) $(EXTRA_LDFLAGS) $(MKSQUASHFS_OBJS) $(LIBS) -o $@
mksquashfs.o: Makefile mksquashfs.c squashfs_fs.h squashfs_swap.h mksquashfs.h \
sort.h pseudo.h compressor.h xattr.h action.h error.h progressbar.h \
info.h caches-queues-lists.h read_fs.h restore.h process_fragments.h
read_fs.o: read_fs.c squashfs_fs.h squashfs_swap.h compressor.h xattr.h \
error.h mksquashfs.h
sort.o: sort.c squashfs_fs.h mksquashfs.h sort.h error.h progressbar.h
swap.o: swap.c
pseudo.o: pseudo.c pseudo.h error.h progressbar.h
compressor.o: Makefile compressor.c compressor.h squashfs_fs.h
xattr.o: xattr.c squashfs_fs.h squashfs_swap.h mksquashfs.h xattr.h error.h \
progressbar.h
read_xattrs.o: read_xattrs.c squashfs_fs.h squashfs_swap.h xattr.h error.h
action.o: action.c squashfs_fs.h mksquashfs.h action.h error.h
progressbar.o: progressbar.c error.h
read_file.o: read_file.c error.h
info.o: info.c squashfs_fs.h mksquashfs.h error.h progressbar.h \
caches-queues-lists.h
restore.o: restore.c caches-queues-lists.h squashfs_fs.h mksquashfs.h error.h \
progressbar.h info.h
process_fragments.o: process_fragments.c process_fragments.h
caches-queues-lists.o: caches-queues-lists.c error.h caches-queues-lists.h
gzip_wrapper.o: gzip_wrapper.c squashfs_fs.h gzip_wrapper.h compressor.h
lzma_wrapper.o: lzma_wrapper.c compressor.h squashfs_fs.h
lzma_xz_wrapper.o: lzma_xz_wrapper.c compressor.h squashfs_fs.h
lzo_wrapper.o: lzo_wrapper.c squashfs_fs.h lzo_wrapper.h compressor.h
lz4_wrapper.o: lz4_wrapper.c squashfs_fs.h lz4_wrapper.h compressor.h
xz_wrapper.o: xz_wrapper.c squashfs_fs.h xz_wrapper.h compressor.h
unsquashfs: $(UNSQUASHFS_OBJS)
$(CC) $(LDFLAGS) $(EXTRA_LDFLAGS) $(UNSQUASHFS_OBJS) $(LIBS) -o $@
unsquashfs.o: unsquashfs.h unsquashfs.c squashfs_fs.h squashfs_swap.h \
squashfs_compat.h xattr.h read_fs.h compressor.h
unsquash-1.o: unsquashfs.h unsquash-1.c squashfs_fs.h squashfs_compat.h
unsquash-2.o: unsquashfs.h unsquash-2.c squashfs_fs.h squashfs_compat.h
unsquash-3.o: unsquashfs.h unsquash-3.c squashfs_fs.h squashfs_compat.h
unsquash-4.o: unsquashfs.h unsquash-4.c squashfs_fs.h squashfs_swap.h \
read_fs.h
unsquash-123.o: unsquashfs.h unsquash-123.c squashfs_fs.h squashfs_compat.h
unsquash-34.o: unsquashfs.h unsquash-34.c
unsquashfs_xattr.o: unsquashfs_xattr.c unsquashfs.h squashfs_fs.h xattr.h
unsquashfs_info.o: unsquashfs.h squashfs_fs.h
.PHONY: clean
clean:
-rm -f *.o mksquashfs unsquashfs
.PHONY: install
install: mksquashfs unsquashfs
mkdir -p $(INSTALL_DIR)
cp mksquashfs $(INSTALL_DIR)
cp unsquashfs $(INSTALL_DIR)

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,328 @@
#ifndef ACTION_H
#define ACTION_H
/*
* Create a squashfs filesystem. This is a highly compressed read only
* filesystem.
*
* Copyright (c) 2011, 2012, 2013, 2014
* 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.
*
* action.h
*/
/*
* Lexical analyser definitions
*/
#define TOK_OPEN_BRACKET 0
#define TOK_CLOSE_BRACKET 1
#define TOK_AND 2
#define TOK_OR 3
#define TOK_NOT 4
#define TOK_COMMA 5
#define TOK_AT 6
#define TOK_WHITE_SPACE 7
#define TOK_STRING 8
#define TOK_EOF 9
#define TOK_TO_STR(OP, S) ({ \
char *s; \
switch(OP) { \
case TOK_EOF: \
s = "EOF"; \
break; \
case TOK_STRING: \
s = S; \
break; \
default: \
s = token_table[OP].string; \
break; \
} \
s; \
})
struct token_entry {
char *string;
int token;
int size;
};
/*
* Expression parser definitions
*/
#define OP_TYPE 0
#define ATOM_TYPE 1
#define UNARY_TYPE 2
#define SYNTAX_ERROR(S, ARGS...) { \
char *src = strdup(source); \
src[cur_ptr - source] = '\0'; \
fprintf(stderr, "Failed to parse action \"%s\"\n", source); \
fprintf(stderr, "Syntax error: "S, ##ARGS); \
fprintf(stderr, "Got here \"%s\"\n", src); \
free(src); \
}
#define TEST_SYNTAX_ERROR(TEST, ARG, S, ARGS...) { \
char *src = strdup(source); \
src[cur_ptr - source] = '\0'; \
fprintf(stderr, "Failed to parse action \"%s\"\n", source); \
fprintf(stderr, "Syntax error in \"%s()\", arg %d: "S, TEST->name, \
ARG, ##ARGS); \
fprintf(stderr, "Got here \"%s\"\n", src); \
free(src); \
}
struct expr;
struct expr_op {
struct expr *lhs;
struct expr *rhs;
int op;
};
struct atom {
struct test_entry *test;
int args;
char **argv;
void *data;
};
struct unary_op {
struct expr *expr;
int op;
};
struct expr {
int type;
union {
struct atom atom;
struct expr_op expr_op;
struct unary_op unary_op;
};
};
/*
* Test operation definitions
*/
#define NUM_EQ 1
#define NUM_LESS 2
#define NUM_GREATER 3
struct test_number_arg {
long long size;
int range;
};
struct test_range_args {
long long start;
long long end;
};
struct action;
struct action_data;
struct test_entry {
char *name;
int args;
int (*fn)(struct atom *, struct action_data *);
int (*parse_args)(struct test_entry *, struct atom *);
int exclude_ok;
int handle_logging;
};
/*
* Type test specific definitions
*/
struct type_entry {
int value;
char type;
};
/*
* Action definitions
*/
#define FRAGMENT_ACTION 0
#define EXCLUDE_ACTION 1
#define FRAGMENTS_ACTION 2
#define NO_FRAGMENTS_ACTION 3
#define ALWAYS_FRAGS_ACTION 4
#define NO_ALWAYS_FRAGS_ACTION 5
#define COMPRESSED_ACTION 6
#define UNCOMPRESSED_ACTION 7
#define UID_ACTION 8
#define GID_ACTION 9
#define GUID_ACTION 10
#define MODE_ACTION 11
#define EMPTY_ACTION 12
#define MOVE_ACTION 13
#define PRUNE_ACTION 14
#define NOOP_ACTION 15
/*
* Define what file types each action operates over
*/
#define ACTION_DIR 1
#define ACTION_REG 2
#define ACTION_ALL_LNK 3
#define ACTION_ALL 4
#define ACTION_LNK 5
/*
* Action logging requested, specified by the various
* -action, -true-action, -false-action and -verbose-action
* options
*/
#define ACTION_LOG_NONE 0
#define ACTION_LOG_TRUE 1
#define ACTION_LOG_FALSE 2
#define ACTION_LOG_VERBOSE ACTION_LOG_TRUE | ACTION_LOG_FALSE
struct action_entry {
char *name;
int type;
int args;
int file_types;
int (*parse_args)(struct action_entry *, int, char **, void **);
void (*run_action)(struct action *, struct dir_ent *);
};
struct action_data {
int depth;
char *name;
char *pathname;
char *subpath;
struct stat *buf;
struct dir_ent *dir_ent;
struct dir_info *root;
};
struct action {
int type;
struct action_entry *action;
int args;
char **argv;
struct expr *expr;
void *data;
int verbose;
};
/*
* Uid/gid action specific definitions
*/
struct uid_info {
uid_t uid;
};
struct gid_info {
gid_t gid;
};
struct guid_info {
uid_t uid;
gid_t gid;
};
/*
* Mode action specific definitions
*/
#define ACTION_MODE_SET 0
#define ACTION_MODE_ADD 1
#define ACTION_MODE_REM 2
#define ACTION_MODE_OCT 3
struct mode_data {
struct mode_data *next;
int operation;
int mode;
unsigned int mask;
char X;
};
/*
* Empty action specific definitions
*/
#define EMPTY_ALL 0
#define EMPTY_SOURCE 1
#define EMPTY_EXCLUDED 2
struct empty_data {
int val;
};
/*
* Move action specific definitions
*/
#define ACTION_MOVE_RENAME 1
#define ACTION_MOVE_MOVE 2
struct move_ent {
int ops;
struct dir_ent *dir_ent;
char *name;
struct dir_info *dest;
struct move_ent *next;
};
/*
* Perm test function specific definitions
*/
#define PERM_ALL 1
#define PERM_ANY 2
#define PERM_EXACT 3
struct perm_data {
int op;
int mode;
};
/*
* External function definitions
*/
extern int parse_action(char *, int verbose);
extern void dump_actions();
extern void *eval_frag_actions(struct dir_info *, struct dir_ent *);
extern void *get_frag_action(void *);
extern int eval_exclude_actions(char *, char *, char *, struct stat *, int,
struct dir_ent *);
extern void eval_actions(struct dir_info *, struct dir_ent *);
extern int eval_empty_actions(struct dir_info *, struct dir_ent *dir_ent);
extern void eval_move_actions(struct dir_info *, struct dir_ent *);
extern int eval_prune_actions(struct dir_info *, struct dir_ent *);
extern void do_move_actions();
extern int read_bytes(int, void *, int);
extern int actions();
extern int move_actions();
extern int empty_actions();
extern int read_action_file(char *, int);
extern int exclude_actions();
extern int prune_actions();
#endif

View file

@ -0,0 +1,35 @@
#!/bin/bash
export LZMA_LIBDIR=$PWD/../../LIB/LZMA
export LZ4_LIBDIR=$PWD/../../LIB/LZ4
export ZSTD_LIBDIR=$PWD/../../LIB/ZSTD
export LZO_LIBDIR=$PWD/../../LIB/LZO
if [ -e /lib64/libz.a ]; then
export VTZLIB=/lib64/libz.a
elif [ -e /lib/libz.a ]; then
export VTZLIB=/lib/libz.a
elif [ -e /usr/lib/libz.a ]; then
export VTZLIB=/usr/lib/libz.a
fi
rm -f unsquashfs
make clean
make -e unsquashfs
if [ -e unsquashfs ]; then
strip --strip-all unsquashfs
echo -e "\n========== SUCCESS ============\n"
else
echo -e "\n========== FAILED ============\n"
fi
if uname -a | egrep -q 'x86_64|amd64'; then
name=unsquashfs_64
else
name=unsquashfs_32
fi
rm -f ../../$name
cp -a unsquashfs ../../$name

View file

@ -0,0 +1,642 @@
/*
* Create a squashfs filesystem. This is a highly compressed read only
* filesystem.
*
* Copyright (c) 2013, 2014, 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.
*
* caches-queues-lists.c
*/
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "error.h"
#include "caches-queues-lists.h"
extern int add_overflow(int, int);
extern int multiply_overflow(int, int);
#define TRUE 1
#define FALSE 0
struct queue *queue_init(int size)
{
struct queue *queue = malloc(sizeof(struct queue));
if(queue == NULL)
MEM_ERROR();
if(add_overflow(size, 1) ||
multiply_overflow(size + 1, sizeof(void *)))
BAD_ERROR("Size too large in queue_init\n");
queue->data = malloc(sizeof(void *) * (size + 1));
if(queue->data == NULL)
MEM_ERROR();
queue->size = size + 1;
queue->readp = queue->writep = 0;
pthread_mutex_init(&queue->mutex, NULL);
pthread_cond_init(&queue->empty, NULL);
pthread_cond_init(&queue->full, NULL);
return queue;
}
void queue_put(struct queue *queue, void *data)
{
int nextp;
pthread_cleanup_push((void *) pthread_mutex_unlock, &queue->mutex);
pthread_mutex_lock(&queue->mutex);
while((nextp = (queue->writep + 1) % queue->size) == queue->readp)
pthread_cond_wait(&queue->full, &queue->mutex);
queue->data[queue->writep] = data;
queue->writep = nextp;
pthread_cond_signal(&queue->empty);
pthread_cleanup_pop(1);
}
void *queue_get(struct queue *queue)
{
void *data;
pthread_cleanup_push((void *) pthread_mutex_unlock, &queue->mutex);
pthread_mutex_lock(&queue->mutex);
while(queue->readp == queue->writep)
pthread_cond_wait(&queue->empty, &queue->mutex);
data = queue->data[queue->readp];
queue->readp = (queue->readp + 1) % queue->size;
pthread_cond_signal(&queue->full);
pthread_cleanup_pop(1);
return data;
}
int queue_empty(struct queue *queue)
{
int empty;
pthread_cleanup_push((void *) pthread_mutex_unlock, &queue->mutex);
pthread_mutex_lock(&queue->mutex);
empty = queue->readp == queue->writep;
pthread_cleanup_pop(1);
return empty;
}
void queue_flush(struct queue *queue)
{
pthread_cleanup_push((void *) pthread_mutex_unlock, &queue->mutex);
pthread_mutex_lock(&queue->mutex);
queue->readp = queue->writep;
pthread_cleanup_pop(1);
}
void dump_queue(struct queue *queue)
{
pthread_cleanup_push((void *) pthread_mutex_unlock, &queue->mutex);
pthread_mutex_lock(&queue->mutex);
printf("\tMax size %d, size %d%s\n", queue->size - 1,
queue->readp <= queue->writep ? queue->writep - queue->readp :
queue->size - queue->readp + queue->writep,
queue->readp == queue->writep ? " (EMPTY)" :
((queue->writep + 1) % queue->size) == queue->readp ?
" (FULL)" : "");
pthread_cleanup_pop(1);
}
/* define seq queue hash tables */
#define CALCULATE_SEQ_HASH(N) CALCULATE_HASH(N)
/* Called with the seq queue mutex held */
INSERT_HASH_TABLE(seq, struct seq_queue, CALCULATE_SEQ_HASH, sequence, seq)
/* Called with the cache mutex held */
REMOVE_HASH_TABLE(seq, struct seq_queue, CALCULATE_SEQ_HASH, sequence, seq);
struct seq_queue *seq_queue_init()
{
struct seq_queue *queue = malloc(sizeof(struct seq_queue));
if(queue == NULL)
MEM_ERROR();
memset(queue, 0, sizeof(struct seq_queue));
pthread_mutex_init(&queue->mutex, NULL);
pthread_cond_init(&queue->wait, NULL);
return queue;
}
void seq_queue_put(struct seq_queue *queue, struct file_buffer *entry)
{
pthread_cleanup_push((void *) pthread_mutex_unlock, &queue->mutex);
pthread_mutex_lock(&queue->mutex);
insert_seq_hash_table(queue, entry);
if(entry->fragment)
queue->fragment_count ++;
else
queue->block_count ++;
if(entry->sequence == queue->sequence)
pthread_cond_signal(&queue->wait);
pthread_cleanup_pop(1);
}
struct file_buffer *seq_queue_get(struct seq_queue *queue)
{
/*
* Return next buffer from queue in sequence order (queue->sequence). If
* found return it, otherwise wait for it to arrive.
*/
int hash = CALCULATE_SEQ_HASH(queue->sequence);
struct file_buffer *entry;
pthread_cleanup_push((void *) pthread_mutex_unlock, &queue->mutex);
pthread_mutex_lock(&queue->mutex);
while(1) {
for(entry = queue->hash_table[hash]; entry;
entry = entry->seq_next)
if(entry->sequence == queue->sequence)
break;
if(entry) {
/*
* found the buffer in the queue, decrement the
* appropriate count, and remove from hash list
*/
if(entry->fragment)
queue->fragment_count --;
else
queue->block_count --;
remove_seq_hash_table(queue, entry);
queue->sequence ++;
break;
}
/* entry not found, wait for it to arrive */
pthread_cond_wait(&queue->wait, &queue->mutex);
}
pthread_cleanup_pop(1);
return entry;
}
void seq_queue_flush(struct seq_queue *queue)
{
int i;
pthread_cleanup_push((void *) pthread_mutex_unlock, &queue->mutex);
pthread_mutex_lock(&queue->mutex);
for(i = 0; i < HASH_SIZE; i++)
queue->hash_table[i] = NULL;
queue->fragment_count = queue->block_count = 0;
pthread_cleanup_pop(1);
}
void dump_seq_queue(struct seq_queue *queue, int fragment_queue)
{
int size;
pthread_cleanup_push((void *) pthread_mutex_unlock, &queue->mutex);
pthread_mutex_lock(&queue->mutex);
size = fragment_queue ? queue->fragment_count : queue->block_count;
printf("\tMax size unlimited, size %d%s\n", size,
size == 0 ? " (EMPTY)" : "");
pthread_cleanup_pop(1);
}
/* define cache hash tables */
#define CALCULATE_CACHE_HASH(N) CALCULATE_HASH(llabs(N))
/* Called with the cache mutex held */
INSERT_HASH_TABLE(cache, struct cache, CALCULATE_CACHE_HASH, index, hash)
/* Called with the cache mutex held */
REMOVE_HASH_TABLE(cache, struct cache, CALCULATE_CACHE_HASH, index, hash);
/* define cache free list */
/* Called with the cache mutex held */
INSERT_LIST(free, struct file_buffer)
/* Called with the cache mutex held */
REMOVE_LIST(free, struct file_buffer)
struct cache *cache_init(int buffer_size, int max_buffers, int noshrink_lookup,
int first_freelist)
{
struct cache *cache = malloc(sizeof(struct cache));
if(cache == NULL)
MEM_ERROR();
cache->max_buffers = max_buffers;
cache->buffer_size = buffer_size;
cache->count = 0;
cache->used = 0;
cache->free_list = NULL;
/*
* The cache will grow up to max_buffers in size in response to
* an increase in readhead/number of buffers in flight. But
* once the outstanding buffers gets returned, we can either elect
* to shrink the cache, or to put the freed blocks onto a free list.
*
* For the caches where we want to do lookup (fragment/writer),
* a don't shrink policy is best, for the reader cache it
* makes no sense to keep buffers around longer than necessary as
* we don't do any lookup on those blocks.
*/
cache->noshrink_lookup = noshrink_lookup;
/*
* The default use freelist before growing cache policy behaves
* poorly with appending - with many duplicates the caches
* do not grow due to the fact that large queues of outstanding
* fragments/writer blocks do not occur, leading to small caches
* and un-uncessary performance loss to frequent cache
* replacement in the small caches. Therefore with appending
* change the policy to grow the caches before reusing blocks
* from the freelist
*/
cache->first_freelist = first_freelist;
memset(cache->hash_table, 0, sizeof(struct file_buffer *) * 65536);
pthread_mutex_init(&cache->mutex, NULL);
pthread_cond_init(&cache->wait_for_free, NULL);
pthread_cond_init(&cache->wait_for_unlock, NULL);
return cache;
}
struct file_buffer *cache_lookup(struct cache *cache, long long index)
{
/* Lookup block in the cache, if found return with usage count
* incremented, if not found return NULL */
int hash = CALCULATE_CACHE_HASH(index);
struct file_buffer *entry;
pthread_cleanup_push((void *) pthread_mutex_unlock, &cache->mutex);
pthread_mutex_lock(&cache->mutex);
for(entry = cache->hash_table[hash]; entry; entry = entry->hash_next)
if(entry->index == index)
break;
if(entry) {
/* found the block in the cache, increment used count and
* if necessary remove from free list so it won't disappear
*/
if(entry->used == 0) {
remove_free_list(&cache->free_list, entry);
cache->used ++;
}
entry->used ++;
}
pthread_cleanup_pop(1);
return entry;
}
static struct file_buffer *cache_freelist(struct cache *cache)
{
struct file_buffer *entry = cache->free_list;
remove_free_list(&cache->free_list, entry);
/* a block on the free_list is hashed */
remove_cache_hash_table(cache, entry);
cache->used ++;
return entry;
}
static struct file_buffer *cache_alloc(struct cache *cache)
{
struct file_buffer *entry = malloc(sizeof(struct file_buffer) +
cache->buffer_size);
if(entry == NULL)
MEM_ERROR();
entry->cache = cache;
entry->free_prev = entry->free_next = NULL;
cache->count ++;
return entry;
}
static struct file_buffer *_cache_get(struct cache *cache, long long index,
int hash)
{
/* Get a free block out of the cache indexed on index. */
struct file_buffer *entry = NULL;
pthread_cleanup_push((void *) pthread_mutex_unlock, &cache->mutex);
pthread_mutex_lock(&cache->mutex);
while(1) {
if(cache->noshrink_lookup) {
/* first try to get a block from the free list */
if(cache->first_freelist && cache->free_list)
entry = cache_freelist(cache);
else if(cache->count < cache->max_buffers) {
entry = cache_alloc(cache);
cache->used ++;
} else if(!cache->first_freelist && cache->free_list)
entry = cache_freelist(cache);
} else { /* shrinking non-lookup cache */
if(cache->count < cache->max_buffers) {
entry = cache_alloc(cache);
if(cache->count > cache->max_count)
cache->max_count = cache->count;
}
}
if(entry)
break;
/* wait for a block */
pthread_cond_wait(&cache->wait_for_free, &cache->mutex);
}
/* initialise block and if hash is set insert into the hash table */
entry->used = 1;
entry->locked = FALSE;
entry->wait_on_unlock = FALSE;
entry->error = FALSE;
if(hash) {
entry->index = index;
insert_cache_hash_table(cache, entry);
}
pthread_cleanup_pop(1);
return entry;
}
struct file_buffer *cache_get(struct cache *cache, long long index)
{
return _cache_get(cache, index, 1);
}
struct file_buffer *cache_get_nohash(struct cache *cache)
{
return _cache_get(cache, 0, 0);
}
void cache_hash(struct file_buffer *entry, long long index)
{
struct cache *cache = entry->cache;
pthread_cleanup_push((void *) pthread_mutex_unlock, &cache->mutex);
pthread_mutex_lock(&cache->mutex);
entry->index = index;
insert_cache_hash_table(cache, entry);
pthread_cleanup_pop(1);
}
void cache_block_put(struct file_buffer *entry)
{
struct cache *cache;
/*
* Finished with this cache entry, once the usage count reaches zero it
* can be reused.
*
* If noshrink_lookup is set, put the block onto the free list.
* As blocks remain accessible via the hash table they can be found
* getting a new lease of life before they are reused.
*
* if noshrink_lookup is not set then shrink the cache.
*/
if(entry == NULL)
return;
cache = entry->cache;
pthread_cleanup_push((void *) pthread_mutex_unlock, &cache->mutex);
pthread_mutex_lock(&cache->mutex);
entry->used --;
if(entry->used == 0) {
if(cache->noshrink_lookup) {
insert_free_list(&cache->free_list, entry);
cache->used --;
} else {
free(entry);
cache->count --;
}
/* One or more threads may be waiting on this block */
pthread_cond_signal(&cache->wait_for_free);
}
pthread_cleanup_pop(1);
}
void dump_cache(struct cache *cache)
{
pthread_cleanup_push((void *) pthread_mutex_unlock, &cache->mutex);
pthread_mutex_lock(&cache->mutex);
if(cache->noshrink_lookup)
printf("\tMax buffers %d, Current size %d, Used %d, %s\n",
cache->max_buffers, cache->count, cache->used,
cache->free_list ? "Free buffers" : "No free buffers");
else
printf("\tMax buffers %d, Current size %d, Maximum historical "
"size %d\n", cache->max_buffers, cache->count,
cache->max_count);
pthread_cleanup_pop(1);
}
struct file_buffer *cache_get_nowait(struct cache *cache, long long index)
{
struct file_buffer *entry = NULL;
/*
* block doesn't exist, create it, but return it with the
* locked flag set, so nothing tries to use it while it doesn't
* contain data.
*
* If there's no space in the cache then return NULL.
*/
pthread_cleanup_push((void *) pthread_mutex_unlock, &cache->mutex);
pthread_mutex_lock(&cache->mutex);
/* first try to get a block from the free list */
if(cache->first_freelist && cache->free_list)
entry = cache_freelist(cache);
else if(cache->count < cache->max_buffers) {
entry = cache_alloc(cache);
cache->used ++;
} else if(!cache->first_freelist && cache->free_list)
entry = cache_freelist(cache);
if(entry) {
/* initialise block and insert into the hash table */
entry->used = 1;
entry->locked = TRUE;
entry->wait_on_unlock = FALSE;
entry->error = FALSE;
entry->index = index;
insert_cache_hash_table(cache, entry);
}
pthread_cleanup_pop(1);
return entry;
}
struct file_buffer *cache_lookup_nowait(struct cache *cache, long long index,
char *locked)
{
/*
* Lookup block in the cache, if found return it with the locked flag
* indicating whether it is currently locked. In both cases increment
* the used count.
*
* If it doesn't exist in the cache return NULL;
*/
int hash = CALCULATE_CACHE_HASH(index);
struct file_buffer *entry;
pthread_cleanup_push((void *) pthread_mutex_unlock, &cache->mutex);
pthread_mutex_lock(&cache->mutex);
/* first check if the entry already exists */
for(entry = cache->hash_table[hash]; entry; entry = entry->hash_next)
if(entry->index == index)
break;
if(entry) {
if(entry->used == 0) {
remove_free_list(&cache->free_list, entry);
cache->used ++;
}
entry->used ++;
*locked = entry->locked;
}
pthread_cleanup_pop(1);
return entry;
}
void cache_wait_unlock(struct file_buffer *buffer)
{
struct cache *cache = buffer->cache;
pthread_cleanup_push((void *) pthread_mutex_unlock, &cache->mutex);
pthread_mutex_lock(&cache->mutex);
while(buffer->locked) {
/*
* another thread is filling this in, wait until it
* becomes unlocked. Used has been incremented to ensure it
* doesn't get reused. By definition a block can't be
* locked and unused, and so we don't need to worry
* about it being on the freelist now, but, it may
* become unused when unlocked unless used is
* incremented
*/
buffer->wait_on_unlock = TRUE;
pthread_cond_wait(&cache->wait_for_unlock, &cache->mutex);
}
pthread_cleanup_pop(1);
}
void cache_unlock(struct file_buffer *entry)
{
struct cache *cache = entry->cache;
/*
* Unlock this locked cache entry. If anything is waiting for this
* to become unlocked, wake it up.
*/
pthread_cleanup_push((void *) pthread_mutex_unlock, &cache->mutex);
pthread_mutex_lock(&cache->mutex);
entry->locked = FALSE;
if(entry->wait_on_unlock) {
entry->wait_on_unlock = FALSE;
pthread_cond_broadcast(&cache->wait_for_unlock);
}
pthread_cleanup_pop(1);
}

View file

@ -0,0 +1,199 @@
#ifndef CACHES_QUEUES_LISTS_H
#define CACHES_QUEUES_LISTS_H
/*
* Create a squashfs filesystem. This is a highly compressed read only
* filesystem.
*
* Copyright (c) 2013, 2014, 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.
*
* caches-queues-lists.h
*/
#define INSERT_LIST(NAME, TYPE) \
void insert_##NAME##_list(TYPE **list, TYPE *entry) { \
if(*list) { \
entry->NAME##_next = *list; \
entry->NAME##_prev = (*list)->NAME##_prev; \
(*list)->NAME##_prev->NAME##_next = entry; \
(*list)->NAME##_prev = entry; \
} else { \
*list = entry; \
entry->NAME##_prev = entry->NAME##_next = entry; \
} \
}
#define REMOVE_LIST(NAME, TYPE) \
void remove_##NAME##_list(TYPE **list, TYPE *entry) { \
if(entry->NAME##_prev == entry && entry->NAME##_next == entry) { \
/* only this entry in the list */ \
*list = NULL; \
} else if(entry->NAME##_prev != NULL && entry->NAME##_next != NULL) { \
/* more than one entry in the list */ \
entry->NAME##_next->NAME##_prev = entry->NAME##_prev; \
entry->NAME##_prev->NAME##_next = entry->NAME##_next; \
if(*list == entry) \
*list = entry->NAME##_next; \
} \
entry->NAME##_prev = entry->NAME##_next = NULL; \
}
#define INSERT_HASH_TABLE(NAME, TYPE, HASH_FUNCTION, FIELD, LINK) \
void insert_##NAME##_hash_table(TYPE *container, struct file_buffer *entry) \
{ \
int hash = HASH_FUNCTION(entry->FIELD); \
\
entry->LINK##_next = container->hash_table[hash]; \
container->hash_table[hash] = entry; \
entry->LINK##_prev = NULL; \
if(entry->LINK##_next) \
entry->LINK##_next->LINK##_prev = entry; \
}
#define REMOVE_HASH_TABLE(NAME, TYPE, HASH_FUNCTION, FIELD, LINK) \
void remove_##NAME##_hash_table(TYPE *container, struct file_buffer *entry) \
{ \
if(entry->LINK##_prev) \
entry->LINK##_prev->LINK##_next = entry->LINK##_next; \
else \
container->hash_table[HASH_FUNCTION(entry->FIELD)] = \
entry->LINK##_next; \
if(entry->LINK##_next) \
entry->LINK##_next->LINK##_prev = entry->LINK##_prev; \
\
entry->LINK##_prev = entry->LINK##_next = NULL; \
}
#define HASH_SIZE 65536
#define CALCULATE_HASH(n) ((n) & 0xffff)
/* struct describing a cache entry passed between threads */
struct file_buffer {
long long index;
long long sequence;
long long file_size;
union {
long long block;
unsigned short checksum;
};
struct cache *cache;
union {
struct file_info *dupl_start;
struct file_buffer *hash_next;
};
union {
int duplicate;
struct file_buffer *hash_prev;
};
union {
struct {
struct file_buffer *free_next;
struct file_buffer *free_prev;
};
struct {
struct file_buffer *seq_next;
struct file_buffer *seq_prev;
};
};
int size;
int c_byte;
char used;
char fragment;
char error;
char locked;
char wait_on_unlock;
char noD;
char data[0] __attribute__((aligned));
};
/* struct describing queues used to pass data between threads */
struct queue {
int size;
int readp;
int writep;
pthread_mutex_t mutex;
pthread_cond_t empty;
pthread_cond_t full;
void **data;
};
/*
* struct describing seq_queues used to pass data between the read
* thread and the deflate and main threads
*/
struct seq_queue {
int fragment_count;
int block_count;
long long sequence;
struct file_buffer *hash_table[HASH_SIZE];
pthread_mutex_t mutex;
pthread_cond_t wait;
};
/* Cache status struct. Caches are used to keep
track of memory buffers passed between different threads */
struct cache {
int max_buffers;
int count;
int buffer_size;
int noshrink_lookup;
int first_freelist;
union {
int used;
int max_count;
};
pthread_mutex_t mutex;
pthread_cond_t wait_for_free;
pthread_cond_t wait_for_unlock;
struct file_buffer *free_list;
struct file_buffer *hash_table[HASH_SIZE];
};
extern struct queue *queue_init(int);
extern void queue_put(struct queue *, void *);
extern void *queue_get(struct queue *);
extern int queue_empty(struct queue *);
extern void queue_flush(struct queue *);
extern void dump_queue(struct queue *);
extern struct seq_queue *seq_queue_init();
extern void seq_queue_put(struct seq_queue *, struct file_buffer *);
extern void dump_seq_queue(struct seq_queue *, int);
extern struct file_buffer *seq_queue_get(struct seq_queue *);
extern void seq_queue_flush(struct seq_queue *);
extern struct cache *cache_init(int, int, int, int);
extern struct file_buffer *cache_lookup(struct cache *, long long);
extern struct file_buffer *cache_get(struct cache *, long long);
extern struct file_buffer *cache_get_nohash(struct cache *);
extern void cache_hash(struct file_buffer *, long long);
extern void cache_block_put(struct file_buffer *);
extern void dump_cache(struct cache *);
extern struct file_buffer *cache_get_nowait(struct cache *, long long);
extern struct file_buffer *cache_lookup_nowait(struct cache *, long long,
char *);
extern void cache_wait_unlock(struct file_buffer *);
extern void cache_unlock(struct file_buffer *);
extern int first_freelist;
#endif

View file

@ -0,0 +1,145 @@
/*
*
* Copyright (c) 2009, 2010, 2011
* 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.
*
* compressor.c
*/
#include <stdio.h>
#include <string.h>
#include "compressor.h"
#include "squashfs_fs.h"
#ifndef GZIP_SUPPORT
static struct compressor gzip_comp_ops = {
ZLIB_COMPRESSION, "gzip"
};
#else
extern struct compressor gzip_comp_ops;
#endif
#ifndef LZMA_SUPPORT
static struct compressor lzma_comp_ops = {
LZMA_COMPRESSION, "lzma"
};
#else
extern struct compressor lzma_comp_ops;
#endif
#ifndef LZO_SUPPORT
static struct compressor lzo_comp_ops = {
LZO_COMPRESSION, "lzo"
};
#else
extern struct compressor lzo_comp_ops;
#endif
#ifndef LZ4_SUPPORT
static struct compressor lz4_comp_ops = {
LZ4_COMPRESSION, "lz4"
};
#else
extern struct compressor lz4_comp_ops;
#endif
#ifndef XZ_SUPPORT
static struct compressor xz_comp_ops = {
XZ_COMPRESSION, "xz"
};
#else
extern struct compressor xz_comp_ops;
#endif
#ifndef ZSTD_SUPPORT
static struct compressor zstd_comp_ops = {
ZSTD_COMPRESSION, "zstd"
};
#else
extern struct compressor zstd_comp_ops;
#endif
static struct compressor unknown_comp_ops = {
0, "unknown"
};
struct compressor *compressor[] = {
&gzip_comp_ops,
&lzma_comp_ops,
&lzo_comp_ops,
&lz4_comp_ops,
&xz_comp_ops,
&zstd_comp_ops,
&unknown_comp_ops
};
struct compressor *lookup_compressor(char *name)
{
int i;
for(i = 0; compressor[i]->id; i++)
if(strcmp(compressor[i]->name, name) == 0)
break;
return compressor[i];
}
struct compressor *lookup_compressor_id(int id)
{
int i;
for(i = 0; compressor[i]->id; i++)
if(id == compressor[i]->id)
break;
return compressor[i];
}
void display_compressors(char *indent, char *def_comp)
{
int i;
for(i = 0; compressor[i]->id; i++)
if(compressor[i]->supported)
fprintf(stderr, "%s\t%s%s\n", indent,
compressor[i]->name,
strcmp(compressor[i]->name, def_comp) == 0 ?
" (default)" : "");
}
void display_compressor_usage(char *def_comp)
{
int i;
for(i = 0; compressor[i]->id; i++)
if(compressor[i]->supported) {
char *str = strcmp(compressor[i]->name, def_comp) == 0 ?
" (default)" : "";
if(compressor[i]->usage) {
fprintf(stderr, "\t%s%s\n",
compressor[i]->name, str);
compressor[i]->usage();
} else
fprintf(stderr, "\t%s (no options)%s\n",
compressor[i]->name, str);
}
}

View file

@ -0,0 +1,124 @@
#ifndef COMPRESSOR_H
#define COMPRESSOR_H
/*
*
* Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014
* 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.
*
* compressor.h
*/
struct compressor {
int id;
char *name;
int supported;
int (*init)(void **, int, int);
int (*compress)(void *, void *, void *, int, int, int *);
int (*uncompress)(void *, void *, int, int, int *);
int (*options)(char **, int);
int (*options_post)(int);
void *(*dump_options)(int, int *);
int (*extract_options)(int, void *, int);
int (*check_options)(int, void *, int);
void (*display_options)(void *, int);
void (*usage)();
};
extern struct compressor *lookup_compressor(char *);
extern struct compressor *lookup_compressor_id(int);
extern void display_compressors(char *, char *);
extern void display_compressor_usage(char *);
static inline int compressor_init(struct compressor *comp, void **stream,
int block_size, int datablock)
{
if(comp->init == NULL)
return 0;
return comp->init(stream, block_size, datablock);
}
static inline int compressor_compress(struct compressor *comp, void *strm,
void *dest, void *src, int size, int block_size, int *error)
{
return comp->compress(strm, dest, src, size, block_size, error);
}
static inline int compressor_uncompress(struct compressor *comp, void *dest,
void *src, int size, int block_size, int *error)
{
return comp->uncompress(dest, src, size, block_size, error);
}
/*
* For the following functions please see the lzo, lz4 or xz
* compressors for commented examples of how they are used.
*/
static inline int compressor_options(struct compressor *comp, char *argv[],
int argc)
{
if(comp->options == NULL)
return -1;
return comp->options(argv, argc);
}
static inline int compressor_options_post(struct compressor *comp, int block_size)
{
if(comp->options_post == NULL)
return 0;
return comp->options_post(block_size);
}
static inline void *compressor_dump_options(struct compressor *comp,
int block_size, int *size)
{
if(comp->dump_options == NULL)
return NULL;
return comp->dump_options(block_size, size);
}
static inline int compressor_extract_options(struct compressor *comp,
int block_size, void *buffer, int size)
{
if(comp->extract_options == NULL)
return size ? -1 : 0;
return comp->extract_options(block_size, buffer, size);
}
static inline int compressor_check_options(struct compressor *comp,
int block_size, void *buffer, int size)
{
if(comp->check_options == NULL)
return 0;
return comp->check_options(block_size, buffer, size);
}
static inline void compressor_display_options(struct compressor *comp,
void *buffer, int size)
{
if(comp->display_options != NULL)
comp->display_options(buffer, size);
}
#endif

View file

@ -0,0 +1,106 @@
#ifndef ERROR_H
#define ERROR_H
/*
* Create a squashfs filesystem. This is a highly compressed read only
* filesystem.
*
* Copyright (c) 2012, 2013, 2014, 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.
*
* error.h
*/
extern int exit_on_error;
extern void prep_exit();
extern void progressbar_error(char *fmt, ...);
extern void progressbar_info(char *fmt, ...);
#ifdef SQUASHFS_TRACE
#define TRACE(s, args...) \
do { \
progressbar_info("squashfs: "s, ## args);\
} while(0)
#else
#define TRACE(s, args...)
#endif
#define INFO(s, args...) \
do {\
if(!silent)\
progressbar_info(s, ## args);\
} while(0)
#define ERROR(s, args...) \
do {\
progressbar_error(s, ## args); \
} while(0)
#define ERROR_START(s, args...) \
do { \
disable_progress_bar(); \
fprintf(stderr, s, ## args); \
} while(0)
#define ERROR_EXIT(s, args...) \
do {\
if (exit_on_error) { \
fprintf(stderr, "\n"); \
EXIT_MKSQUASHFS(); \
} else { \
fprintf(stderr, s, ## args); \
enable_progress_bar(); \
} \
} while(0)
#define EXIT_MKSQUASHFS() \
do {\
prep_exit();\
exit(1);\
} while(0)
#define BAD_ERROR(s, args...) \
do {\
progressbar_error("FATAL ERROR:" s, ##args); \
EXIT_MKSQUASHFS();\
} while(0)
#define EXIT_UNSQUASH(s, args...) BAD_ERROR(s, ##args)
#define EXIT_UNSQUASH_IGNORE(s, args...) \
do {\
if(ignore_errors) \
ERROR(s, ##args); \
else \
BAD_ERROR(s, ##args); \
} while(0)
#define EXIT_UNSQUASH_STRICT(s, args...) \
do {\
if(!strict_errors) \
ERROR(s, ##args); \
else \
BAD_ERROR(s, ##args); \
} while(0)
#define MEM_ERROR() \
do {\
progressbar_error("FATAL ERROR: Out of memory (%s)\n", \
__func__); \
EXIT_MKSQUASHFS();\
} while(0)
#endif

View file

@ -0,0 +1,32 @@
#ifndef FNMATCH_COMPAT
#define FNMATCH_COMPAT
/*
* Squashfs
*
* Copyright (c) 2015
* 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.
*
* fnmatch_compat.h
*/
#include <fnmatch.h>
#ifndef FNM_EXTMATCH
#define FNM_EXTMATCH 0
#endif
#endif

View file

@ -0,0 +1,500 @@
/*
* Copyright (c) 2009, 2010, 2013, 2014
* 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.
*
* gzip_wrapper.c
*
* Support for ZLIB compression http://www.zlib.net
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <zlib.h>
#include "squashfs_fs.h"
#include "gzip_wrapper.h"
#include "compressor.h"
static struct strategy strategy[] = {
{ "default", Z_DEFAULT_STRATEGY, 0 },
{ "filtered", Z_FILTERED, 0 },
{ "huffman_only", Z_HUFFMAN_ONLY, 0 },
{ "run_length_encoded", Z_RLE, 0 },
{ "fixed", Z_FIXED, 0 },
{ NULL, 0, 0 }
};
static int strategy_count = 0;
/* default compression level */
static int compression_level = GZIP_DEFAULT_COMPRESSION_LEVEL;
/* default window size */
static int window_size = GZIP_DEFAULT_WINDOW_SIZE;
/*
* This function is called by the options parsing code in mksquashfs.c
* to parse any -X compressor option.
*
* This function returns:
* >=0 (number of additional args parsed) on success
* -1 if the option was unrecognised, or
* -2 if the option was recognised, but otherwise bad in
* some way (e.g. invalid parameter)
*
* Note: this function sets internal compressor state, but does not
* pass back the results of the parsing other than success/failure.
* The gzip_dump_options() function is called later to get the options in
* a format suitable for writing to the filesystem.
*/
static int gzip_options(char *argv[], int argc)
{
if(strcmp(argv[0], "-Xcompression-level") == 0) {
if(argc < 2) {
fprintf(stderr, "gzip: -Xcompression-level missing "
"compression level\n");
fprintf(stderr, "gzip: -Xcompression-level it "
"should be 1 >= n <= 9\n");
goto failed;
}
compression_level = atoi(argv[1]);
if(compression_level < 1 || compression_level > 9) {
fprintf(stderr, "gzip: -Xcompression-level invalid, it "
"should be 1 >= n <= 9\n");
goto failed;
}
return 1;
} else if(strcmp(argv[0], "-Xwindow-size") == 0) {
if(argc < 2) {
fprintf(stderr, "gzip: -Xwindow-size missing window "
" size\n");
fprintf(stderr, "gzip: -Xwindow-size <window-size>\n");
goto failed;
}
window_size = atoi(argv[1]);
if(window_size < 8 || window_size > 15) {
fprintf(stderr, "gzip: -Xwindow-size invalid, it "
"should be 8 >= n <= 15\n");
goto failed;
}
return 1;
} else if(strcmp(argv[0], "-Xstrategy") == 0) {
char *name;
int i;
if(argc < 2) {
fprintf(stderr, "gzip: -Xstrategy missing "
"strategies\n");
goto failed;
}
name = argv[1];
while(name[0] != '\0') {
for(i = 0; strategy[i].name; i++) {
int n = strlen(strategy[i].name);
if((strncmp(name, strategy[i].name, n) == 0) &&
(name[n] == '\0' ||
name[n] == ',')) {
if(strategy[i].selected == 0) {
strategy[i].selected = 1;
strategy_count++;
}
name += name[n] == ',' ? n + 1 : n;
break;
}
}
if(strategy[i].name == NULL) {
fprintf(stderr, "gzip: -Xstrategy unrecognised "
"strategy\n");
goto failed;
}
}
return 1;
}
return -1;
failed:
return -2;
}
/*
* This function is called after all options have been parsed.
* It is used to do post-processing on the compressor options using
* values that were not expected to be known at option parse time.
*
* This function returns 0 on successful post processing, or
* -1 on error
*/
static int gzip_options_post(int block_size)
{
if(strategy_count == 1 && strategy[0].selected) {
strategy_count = 0;
strategy[0].selected = 0;
}
return 0;
}
/*
* This function is called by mksquashfs to dump the parsed
* compressor options in a format suitable for writing to the
* compressor options field in the filesystem (stored immediately
* after the superblock).
*
* This function returns a pointer to the compression options structure
* to be stored (and the size), or NULL if there are no compression
* options
*
*/
static void *gzip_dump_options(int block_size, int *size)
{
static struct gzip_comp_opts comp_opts;
int i, strategies = 0;
/*
* If default compression options of:
* compression-level: 8 and
* window-size: 15 and
* strategy_count == 0 then
* don't store a compression options structure (this is compatible
* with the legacy implementation of GZIP for Squashfs)
*/
if(compression_level == GZIP_DEFAULT_COMPRESSION_LEVEL &&
window_size == GZIP_DEFAULT_WINDOW_SIZE &&
strategy_count == 0)
return NULL;
for(i = 0; strategy[i].name; i++)
strategies |= strategy[i].selected << i;
comp_opts.compression_level = compression_level;
comp_opts.window_size = window_size;
comp_opts.strategy = strategies;
SQUASHFS_INSWAP_COMP_OPTS(&comp_opts);
*size = sizeof(comp_opts);
return &comp_opts;
}
/*
* This function is a helper specifically for the append mode of
* mksquashfs. Its purpose is to set the internal compressor state
* to the stored compressor options in the passed compressor options
* structure.
*
* In effect this function sets up the compressor options
* to the same state they were when the filesystem was originally
* generated, this is to ensure on appending, the compressor uses
* the same compression options that were used to generate the
* original filesystem.
*
* Note, even if there are no compressor options, this function is still
* called with an empty compressor structure (size == 0), to explicitly
* set the default options, this is to ensure any user supplied
* -X options on the appending mksquashfs command line are over-ridden
*
* This function returns 0 on sucessful extraction of options, and
* -1 on error
*/
static int gzip_extract_options(int block_size, void *buffer, int size)
{
struct gzip_comp_opts *comp_opts = buffer;
int i;
if(size == 0) {
/* Set default values */
compression_level = GZIP_DEFAULT_COMPRESSION_LEVEL;
window_size = GZIP_DEFAULT_WINDOW_SIZE;
strategy_count = 0;
return 0;
}
/* we expect a comp_opts structure of sufficient size to be present */
if(size < sizeof(*comp_opts))
goto failed;
SQUASHFS_INSWAP_COMP_OPTS(comp_opts);
/* Check comp_opts structure for correctness */
if(comp_opts->compression_level < 1 ||
comp_opts->compression_level > 9) {
fprintf(stderr, "gzip: bad compression level in "
"compression options structure\n");
goto failed;
}
compression_level = comp_opts->compression_level;
if(comp_opts->window_size < 8 ||
comp_opts->window_size > 15) {
fprintf(stderr, "gzip: bad window size in "
"compression options structure\n");
goto failed;
}
window_size = comp_opts->window_size;
strategy_count = 0;
for(i = 0; strategy[i].name; i++) {
if((comp_opts->strategy >> i) & 1) {
strategy[i].selected = 1;
strategy_count ++;
} else
strategy[i].selected = 0;
}
return 0;
failed:
fprintf(stderr, "gzip: error reading stored compressor options from "
"filesystem!\n");
return -1;
}
static void gzip_display_options(void *buffer, int size)
{
struct gzip_comp_opts *comp_opts = buffer;
int i, printed;
/* we expect a comp_opts structure of sufficient size to be present */
if(size < sizeof(*comp_opts))
goto failed;
SQUASHFS_INSWAP_COMP_OPTS(comp_opts);
/* Check comp_opts structure for correctness */
if(comp_opts->compression_level < 1 ||
comp_opts->compression_level > 9) {
fprintf(stderr, "gzip: bad compression level in "
"compression options structure\n");
goto failed;
}
printf("\tcompression-level %d\n", comp_opts->compression_level);
if(comp_opts->window_size < 8 ||
comp_opts->window_size > 15) {
fprintf(stderr, "gzip: bad window size in "
"compression options structure\n");
goto failed;
}
printf("\twindow-size %d\n", comp_opts->window_size);
for(i = 0, printed = 0; strategy[i].name; i++) {
if((comp_opts->strategy >> i) & 1) {
if(printed)
printf(", ");
else
printf("\tStrategies selected: ");
printf("%s", strategy[i].name);
printed = 1;
}
}
if(!printed)
printf("\tStrategies selected: default\n");
else
printf("\n");
return;
failed:
fprintf(stderr, "gzip: error reading stored compressor options from "
"filesystem!\n");
}
/*
* This function is called by mksquashfs to initialise the
* compressor, before compress() is called.
*
* This function returns 0 on success, and
* -1 on error
*/
static int gzip_init(void **strm, int block_size, int datablock)
{
int i, j, res;
struct gzip_stream *stream;
if(!datablock || !strategy_count) {
stream = malloc(sizeof(*stream) + sizeof(struct gzip_strategy));
if(stream == NULL)
goto failed;
stream->strategies = 1;
stream->strategy[0].strategy = Z_DEFAULT_STRATEGY;
} else {
stream = malloc(sizeof(*stream) +
sizeof(struct gzip_strategy) * strategy_count);
if(stream == NULL)
goto failed;
memset(stream->strategy, 0, sizeof(struct gzip_strategy) *
strategy_count);
stream->strategies = strategy_count;
for(i = 0, j = 0; strategy[i].name; i++) {
if(!strategy[i].selected)
continue;
stream->strategy[j].strategy = strategy[i].strategy;
if(j) {
stream->strategy[j].buffer = malloc(block_size);
if(stream->strategy[j].buffer == NULL)
goto failed2;
}
j++;
}
}
stream->stream.zalloc = Z_NULL;
stream->stream.zfree = Z_NULL;
stream->stream.opaque = 0;
res = deflateInit2(&stream->stream, compression_level, Z_DEFLATED,
window_size, 8, stream->strategy[0].strategy);
if(res != Z_OK)
goto failed2;
*strm = stream;
return 0;
failed2:
for(i = 1; i < stream->strategies; i++)
free(stream->strategy[i].buffer);
free(stream);
failed:
return -1;
}
static int gzip_compress(void *strm, void *d, void *s, int size, int block_size,
int *error)
{
int i, res;
struct gzip_stream *stream = strm;
struct gzip_strategy *selected = NULL;
stream->strategy[0].buffer = d;
for(i = 0; i < stream->strategies; i++) {
struct gzip_strategy *strategy = &stream->strategy[i];
res = deflateReset(&stream->stream);
if(res != Z_OK)
goto failed;
stream->stream.next_in = s;
stream->stream.avail_in = size;
stream->stream.next_out = strategy->buffer;
stream->stream.avail_out = block_size;
if(stream->strategies > 1) {
res = deflateParams(&stream->stream,
compression_level, strategy->strategy);
if(res != Z_OK)
goto failed;
}
res = deflate(&stream->stream, Z_FINISH);
strategy->length = stream->stream.total_out;
if(res == Z_STREAM_END) {
if(!selected || selected->length > strategy->length)
selected = strategy;
} else if(res != Z_OK)
goto failed;
}
if(!selected)
/*
* Output buffer overflow. Return out of buffer space
*/
return 0;
if(selected->buffer != d)
memcpy(d, selected->buffer, selected->length);
return (int) selected->length;
failed:
/*
* All other errors return failure, with the compressor
* specific error code in *error
*/
*error = res;
return -1;
}
static int gzip_uncompress(void *d, void *s, int size, int outsize, int *error)
{
int res;
unsigned long bytes = outsize;
res = uncompress(d, &bytes, s, size);
if(res == Z_OK)
return (int) bytes;
else {
*error = res;
return -1;
}
}
static void gzip_usage()
{
fprintf(stderr, "\t -Xcompression-level <compression-level>\n");
fprintf(stderr, "\t\t<compression-level> should be 1 .. 9 (default "
"%d)\n", GZIP_DEFAULT_COMPRESSION_LEVEL);
fprintf(stderr, "\t -Xwindow-size <window-size>\n");
fprintf(stderr, "\t\t<window-size> should be 8 .. 15 (default "
"%d)\n", GZIP_DEFAULT_WINDOW_SIZE);
fprintf(stderr, "\t -Xstrategy strategy1,strategy2,...,strategyN\n");
fprintf(stderr, "\t\tCompress using strategy1,strategy2,...,strategyN"
" in turn\n");
fprintf(stderr, "\t\tand choose the best compression.\n");
fprintf(stderr, "\t\tAvailable strategies: default, filtered, "
"huffman_only,\n\t\trun_length_encoded and fixed\n");
}
struct compressor gzip_comp_ops = {
.init = gzip_init,
.compress = gzip_compress,
.uncompress = gzip_uncompress,
.options = gzip_options,
.options_post = gzip_options_post,
.dump_options = gzip_dump_options,
.extract_options = gzip_extract_options,
.display_options = gzip_display_options,
.usage = gzip_usage,
.id = ZLIB_COMPRESSION,
.name = "gzip",
.supported = 1
};

View file

@ -0,0 +1,75 @@
#ifndef GZIP_WRAPPER_H
#define GZIP_WRAPPER_H
/*
* Squashfs
*
* Copyright (c) 2014
* 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.
*
* gzip_wrapper.h
*
*/
#ifndef linux
#define __BYTE_ORDER BYTE_ORDER
#define __BIG_ENDIAN BIG_ENDIAN
#define __LITTLE_ENDIAN LITTLE_ENDIAN
#else
#include <endian.h>
#endif
#if __BYTE_ORDER == __BIG_ENDIAN
extern unsigned int inswap_le16(unsigned short);
extern unsigned int inswap_le32(unsigned int);
#define SQUASHFS_INSWAP_COMP_OPTS(s) { \
(s)->compression_level = inswap_le32((s)->compression_level); \
(s)->window_size = inswap_le16((s)->window_size); \
(s)->strategy = inswap_le16((s)->strategy); \
}
#else
#define SQUASHFS_INSWAP_COMP_OPTS(s)
#endif
/* Default compression */
#define GZIP_DEFAULT_COMPRESSION_LEVEL 9
#define GZIP_DEFAULT_WINDOW_SIZE 15
struct gzip_comp_opts {
int compression_level;
short window_size;
short strategy;
};
struct strategy {
char *name;
int strategy;
int selected;
};
struct gzip_strategy {
int strategy;
int length;
void *buffer;
};
struct gzip_stream {
z_stream stream;
int strategies;
struct gzip_strategy strategy[0];
};
#endif

View file

@ -0,0 +1,191 @@
/*
* Create a squashfs filesystem. This is a highly compressed read only
* filesystem.
*
* Copyright (c) 2013, 2014, 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.
*
* info.c
*/
#include <pthread.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <signal.h>
#include <sys/time.h>
#include <stdio.h>
#include <math.h>
#include <stdarg.h>
#include <errno.h>
#include <stdlib.h>
#include <dirent.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include "squashfs_fs.h"
#include "mksquashfs.h"
#include "error.h"
#include "progressbar.h"
#include "caches-queues-lists.h"
static int silent = 0;
static struct dir_ent *ent = NULL;
pthread_t info_thread;
void disable_info()
{
ent = NULL;
}
void update_info(struct dir_ent *dir_ent)
{
ent = dir_ent;
}
void print_filename()
{
struct dir_ent *dir_ent = ent;
if(dir_ent == NULL)
return;
if(dir_ent->our_dir->subpath[0] != '\0')
INFO("%s/%s\n", dir_ent->our_dir->subpath, dir_ent->name);
else
INFO("/%s\n", dir_ent->name);
}
void dump_state()
{
disable_progress_bar();
printf("Queue and Cache status dump\n");
printf("===========================\n");
printf("file buffer queue (reader thread -> deflate thread(s))\n");
dump_queue(to_deflate);
printf("uncompressed fragment queue (reader thread -> fragment"
" thread(s))\n");
dump_queue(to_process_frag);
printf("processed fragment queue (fragment thread(s) -> main"
" thread)\n");
dump_seq_queue(to_main, 1);
printf("compressed block queue (deflate thread(s) -> main thread)\n");
dump_seq_queue(to_main, 0);
printf("uncompressed packed fragment queue (main thread -> fragment"
" deflate thread(s))\n");
dump_queue(to_frag);
if(!reproducible) {
printf("locked frag queue (compressed frags waiting while multi-block"
" file is written)\n");
dump_queue(locked_fragment);
printf("compressed block queue (main & fragment deflate threads(s) ->"
" writer thread)\n");
dump_queue(to_writer);
} else {
printf("compressed fragment queue (fragment deflate threads(s) ->"
"fragment order thread)\n");
dump_seq_queue(to_order, 0);
printf("compressed block queue (main & fragment order threads ->"
" writer thread)\n");
dump_queue(to_writer);
}
printf("read cache (uncompressed blocks read by reader thread)\n");
dump_cache(reader_buffer);
printf("block write cache (compressed blocks waiting for the writer"
" thread)\n");
dump_cache(bwriter_buffer);
printf("fragment write cache (compressed fragments waiting for the"
" writer thread)\n");
dump_cache(fwriter_buffer);
printf("fragment cache (frags waiting to be compressed by fragment"
" deflate thread(s))\n");
dump_cache(fragment_buffer);
printf("fragment reserve cache (avoids pipeline stall if frag cache"
" full in dup check)\n");
dump_cache(reserve_cache);
enable_progress_bar();
}
void *info_thrd(void *arg)
{
sigset_t sigmask;
struct timespec timespec = { .tv_sec = 1, .tv_nsec = 0 };
int sig, waiting = 0;
sigemptyset(&sigmask);
sigaddset(&sigmask, SIGQUIT);
sigaddset(&sigmask, SIGHUP);
while(1) {
if(waiting)
sig = sigtimedwait(&sigmask, NULL, &timespec);
else
sig = sigwaitinfo(&sigmask, NULL);
if(sig == -1) {
switch(errno) {
case EAGAIN:
/* interval timed out */
waiting = 0;
/* FALLTHROUGH */
case EINTR:
/* if waiting, the wait will be longer, but
that's OK */
continue;
default:
BAD_ERROR("sigtimedwait/sigwaitinfo failed "
"because %s\n", strerror(errno));
}
}
if(sig == SIGQUIT && !waiting) {
print_filename();
/* set one second interval period, if ^\ received
within then, dump queue and cache status */
waiting = 1;
} else
dump_state();
}
}
void init_info()
{
pthread_create(&info_thread, NULL, info_thrd, NULL);
}

View file

@ -0,0 +1,30 @@
#ifndef INFO_H
#define INFO_H
/*
* Create a squashfs filesystem. This is a highly compressed read only
* filesystem.
*
* Copyright (c) 2013, 2014
* 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.
*
* info.h
*/
extern void disable_info();
extern void update_info(struct dir_ent *);
extern void init_info();
#endif

View file

@ -0,0 +1,286 @@
/*
* Copyright (c) 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.
*
* lz4_wrapper.c
*
* Support for LZ4 compression http://fastcompression.blogspot.com/p/lz4.html
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <lz4.h>
#include <lz4hc.h>
#include "squashfs_fs.h"
#include "lz4_wrapper.h"
#include "compressor.h"
/* LZ4 1.7.0 introduced new functions, and since r131,
* the older functions produce deprecated warnings.
*
* There are still too many distros using older versions
* to switch to the newer functions, but, the deprecated
* functions may completely disappear. This is a mess.
*
* Support both by checking the library version and
* using shadow definitions
*/
/* Earlier (but > 1.7.0) versions don't define this */
#ifndef LZ4HC_CLEVEL_MAX
#define LZ4HC_CLEVEL_MAX 12
#endif
#if LZ4_VERSION_NUMBER >= 10700
#define COMPRESS(src, dest, size, max) LZ4_compress_default(src, dest, size, max)
#define COMPRESS_HC(src, dest, size, max) LZ4_compress_HC(src, dest, size, max, LZ4HC_CLEVEL_MAX)
#else
#define COMPRESS(src, dest, size, max) LZ4_compress_limitedOutput(src, dest, size, max)
#define COMPRESS_HC(src, dest, size, max) LZ4_compressHC_limitedOutput(src, dest, size, max)
#endif
static int hc = 0;
/*
* This function is called by the options parsing code in mksquashfs.c
* to parse any -X compressor option.
*
* This function returns:
* >=0 (number of additional args parsed) on success
* -1 if the option was unrecognised, or
* -2 if the option was recognised, but otherwise bad in
* some way (e.g. invalid parameter)
*
* Note: this function sets internal compressor state, but does not
* pass back the results of the parsing other than success/failure.
* The lz4_dump_options() function is called later to get the options in
* a format suitable for writing to the filesystem.
*/
static int lz4_options(char *argv[], int argc)
{
if(strcmp(argv[0], "-Xhc") == 0) {
hc = 1;
return 0;
}
return -1;
}
/*
* This function is called by mksquashfs to dump the parsed
* compressor options in a format suitable for writing to the
* compressor options field in the filesystem (stored immediately
* after the superblock).
*
* This function returns a pointer to the compression options structure
* to be stored (and the size), or NULL if there are no compression
* options
*
* Currently LZ4 always returns a comp_opts structure, with
* the version indicating LZ4_LEGACY stream fomat. This is to
* easily accomodate changes in the kernel code to different
* stream formats
*/
static void *lz4_dump_options(int block_size, int *size)
{
static struct lz4_comp_opts comp_opts;
comp_opts.version = LZ4_LEGACY;
comp_opts.flags = hc ? LZ4_HC : 0;
SQUASHFS_INSWAP_COMP_OPTS(&comp_opts);
*size = sizeof(comp_opts);
return &comp_opts;
}
/*
* This function is a helper specifically for the append mode of
* mksquashfs. Its purpose is to set the internal compressor state
* to the stored compressor options in the passed compressor options
* structure.
*
* In effect this function sets up the compressor options
* to the same state they were when the filesystem was originally
* generated, this is to ensure on appending, the compressor uses
* the same compression options that were used to generate the
* original filesystem.
*
* Note, even if there are no compressor options, this function is still
* called with an empty compressor structure (size == 0), to explicitly
* set the default options, this is to ensure any user supplied
* -X options on the appending mksquashfs command line are over-ridden
*
* This function returns 0 on sucessful extraction of options, and
* -1 on error
*/
static int lz4_extract_options(int block_size, void *buffer, int size)
{
struct lz4_comp_opts *comp_opts = buffer;
/* we expect a comp_opts structure to be present */
if(size < sizeof(*comp_opts))
goto failed;
SQUASHFS_INSWAP_COMP_OPTS(comp_opts);
/* we expect the stream format to be LZ4_LEGACY */
if(comp_opts->version != LZ4_LEGACY) {
fprintf(stderr, "lz4: unknown LZ4 version\n");
goto failed;
}
/*
* Check compression flags, currently only LZ4_HC ("high compression")
* can be set.
*/
if(comp_opts->flags == LZ4_HC)
hc = 1;
else if(comp_opts->flags != 0) {
fprintf(stderr, "lz4: unknown LZ4 flags\n");
goto failed;
}
return 0;
failed:
fprintf(stderr, "lz4: error reading stored compressor options from "
"filesystem!\n");
return -1;
}
/*
* This function is a helper specifically for unsquashfs.
* Its purpose is to check that the compression options are
* understood by this version of LZ4.
*
* This is important for LZ4 because the format understood by the
* Linux kernel may change from the already obsolete legacy format
* currently supported.
*
* If this does happen, then this version of LZ4 will not be able to decode
* the newer format. So we need to check for this.
*
* This function returns 0 on sucessful checking of options, and
* -1 on error
*/
static int lz4_check_options(int block_size, void *buffer, int size)
{
struct lz4_comp_opts *comp_opts = buffer;
/* we expect a comp_opts structure to be present */
if(size < sizeof(*comp_opts))
goto failed;
SQUASHFS_INSWAP_COMP_OPTS(comp_opts);
/* we expect the stream format to be LZ4_LEGACY */
if(comp_opts->version != LZ4_LEGACY) {
fprintf(stderr, "lz4: unknown LZ4 version\n");
goto failed;
}
return 0;
failed:
fprintf(stderr, "lz4: error reading stored compressor options from "
"filesystem!\n");
return -1;
}
static void lz4_display_options(void *buffer, int size)
{
struct lz4_comp_opts *comp_opts = buffer;
/* check passed comp opts struct is of the correct length */
if(size < sizeof(*comp_opts))
goto failed;
SQUASHFS_INSWAP_COMP_OPTS(comp_opts);
/* we expect the stream format to be LZ4_LEGACY */
if(comp_opts->version != LZ4_LEGACY) {
fprintf(stderr, "lz4: unknown LZ4 version\n");
goto failed;
}
/*
* Check compression flags, currently only LZ4_HC ("high compression")
* can be set.
*/
if(comp_opts->flags & ~LZ4_FLAGS_MASK) {
fprintf(stderr, "lz4: unknown LZ4 flags\n");
goto failed;
}
if(comp_opts->flags & LZ4_HC)
printf("\tHigh Compression option specified (-Xhc)\n");
return;
failed:
fprintf(stderr, "lz4: error reading stored compressor options from "
"filesystem!\n");
}
static int lz4_compress(void *strm, void *dest, void *src, int size,
int block_size, int *error)
{
return 0;
}
static int lz4_uncompress(void *dest, void *src, int size, int outsize,
int *error)
{
int res = LZ4_decompress_safe(src, dest, size, outsize);
if(res < 0) {
*error = res;
return -1;
}
return res;
}
static void lz4_usage()
{
fprintf(stderr, "\t -Xhc\n");
fprintf(stderr, "\t\tCompress using LZ4 High Compression\n");
}
struct compressor lz4_comp_ops = {
.compress = lz4_compress,
.uncompress = lz4_uncompress,
.options = lz4_options,
.dump_options = lz4_dump_options,
.extract_options = lz4_extract_options,
.check_options = lz4_check_options,
.display_options = lz4_display_options,
.usage = lz4_usage,
.id = LZ4_COMPRESSION,
.name = "lz4",
.supported = 1
};

View file

@ -0,0 +1,61 @@
#ifndef LZ4_WRAPPER_H
#define LZ4_WRAPPER_H
/*
* Squashfs
*
* Copyright (c) 2013
* 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.
*
* lz4_wrapper.h
*
*/
#ifndef linux
#define __BYTE_ORDER BYTE_ORDER
#define __BIG_ENDIAN BIG_ENDIAN
#define __LITTLE_ENDIAN LITTLE_ENDIAN
#else
#include <endian.h>
#endif
#if __BYTE_ORDER == __BIG_ENDIAN
extern unsigned int inswap_le32(unsigned int);
#define SQUASHFS_INSWAP_COMP_OPTS(s) { \
(s)->version = inswap_le32((s)->version); \
(s)->flags = inswap_le32((s)->flags); \
}
#else
#define SQUASHFS_INSWAP_COMP_OPTS(s)
#endif
/*
* Define the various stream formats recognised.
* Currently omly legacy stream format is supported by the
* kernel
*/
#define LZ4_LEGACY 1
#define LZ4_FLAGS_MASK 1
/* Define the compression flags recognised. */
#define LZ4_HC 1
struct lz4_comp_opts {
int version;
int flags;
};
#endif

View file

@ -0,0 +1,78 @@
/*
* Copyright (c) 2009, 2010, 2013
* 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.
*
* lzma_wrapper.c
*
* Support for LZMA1 compression using LZMA SDK (4.65 used in
* development, other versions may work) http://www.7-zip.org/sdk.html
*/
#include <LzmaLib.h>
#include "squashfs_fs.h"
#include "compressor.h"
#define LZMA_HEADER_SIZE (LZMA_PROPS_SIZE + 8)
static int lzma_compress(void *strm, void *dest, void *src, int size, int block_size,
int *error)
{
return 0;
}
static int lzma_uncompress(void *dest, void *src, int size, int outsize,
int *error)
{
unsigned char *s = src;
size_t outlen, inlen = size - LZMA_HEADER_SIZE;
int res;
outlen = s[LZMA_PROPS_SIZE] |
(s[LZMA_PROPS_SIZE + 1] << 8) |
(s[LZMA_PROPS_SIZE + 2] << 16) |
(s[LZMA_PROPS_SIZE + 3] << 24);
if(outlen > outsize) {
*error = 0;
return -1;
}
res = LzmaUncompress(dest, &outlen, src + LZMA_HEADER_SIZE, &inlen, src,
LZMA_PROPS_SIZE);
if(res == SZ_OK)
return outlen;
else {
*error = res;
return -1;
}
}
struct compressor lzma_comp_ops = {
.init = NULL,
.compress = lzma_compress,
.uncompress = lzma_uncompress,
.options = NULL,
.usage = NULL,
.id = LZMA_COMPRESSION,
.name = "lzma",
.supported = 1
};

View file

@ -0,0 +1,163 @@
/*
* Copyright (c) 2010, 2013
* 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.
*
* lzma_xz_wrapper.c
*
* Support for LZMA1 compression using XZ Utils liblzma http://tukaani.org/xz/
*/
#include <stdio.h>
#include <string.h>
#include <lzma.h>
#include "squashfs_fs.h"
#include "compressor.h"
#define LZMA_PROPS_SIZE 5
#define LZMA_UNCOMP_SIZE 8
#define LZMA_HEADER_SIZE (LZMA_PROPS_SIZE + LZMA_UNCOMP_SIZE)
#define LZMA_OPTIONS 5
#define MEMLIMIT (32 * 1024 * 1024)
static int lzma_compress(void *dummy, void *dest, void *src, int size,
int block_size, int *error)
{
unsigned char *d = (unsigned char *) dest;
lzma_options_lzma opt;
lzma_stream strm = LZMA_STREAM_INIT;
int res;
lzma_lzma_preset(&opt, LZMA_OPTIONS);
opt.dict_size = block_size;
res = lzma_alone_encoder(&strm, &opt);
if(res != LZMA_OK) {
lzma_end(&strm);
goto failed;
}
strm.next_out = dest;
strm.avail_out = block_size;
strm.next_in = src;
strm.avail_in = size;
res = lzma_code(&strm, LZMA_FINISH);
lzma_end(&strm);
if(res == LZMA_STREAM_END) {
/*
* Fill in the 8 byte little endian uncompressed size field in
* the LZMA header. 8 bytes is excessively large for squashfs
* but this is the standard LZMA header and which is expected by
* the kernel code
*/
d[LZMA_PROPS_SIZE] = size & 255;
d[LZMA_PROPS_SIZE + 1] = (size >> 8) & 255;
d[LZMA_PROPS_SIZE + 2] = (size >> 16) & 255;
d[LZMA_PROPS_SIZE + 3] = (size >> 24) & 255;
d[LZMA_PROPS_SIZE + 4] = 0;
d[LZMA_PROPS_SIZE + 5] = 0;
d[LZMA_PROPS_SIZE + 6] = 0;
d[LZMA_PROPS_SIZE + 7] = 0;
return (int) strm.total_out;
}
if(res == LZMA_OK)
/*
* Output buffer overflow. Return out of buffer space
*/
return 0;
failed:
/*
* All other errors return failure, with the compressor
* specific error code in *error
*/
*error = res;
return -1;
}
static int lzma_uncompress(void *dest, void *src, int size, int outsize,
int *error)
{
lzma_stream strm = LZMA_STREAM_INIT;
int uncompressed_size = 0, res;
unsigned char lzma_header[LZMA_HEADER_SIZE];
res = lzma_alone_decoder(&strm, MEMLIMIT);
if(res != LZMA_OK) {
lzma_end(&strm);
goto failed;
}
memcpy(lzma_header, src, LZMA_HEADER_SIZE);
uncompressed_size = lzma_header[LZMA_PROPS_SIZE] |
(lzma_header[LZMA_PROPS_SIZE + 1] << 8) |
(lzma_header[LZMA_PROPS_SIZE + 2] << 16) |
(lzma_header[LZMA_PROPS_SIZE + 3] << 24);
if(uncompressed_size > outsize) {
res = 0;
goto failed;
}
memset(lzma_header + LZMA_PROPS_SIZE, 255, LZMA_UNCOMP_SIZE);
strm.next_out = dest;
strm.avail_out = outsize;
strm.next_in = lzma_header;
strm.avail_in = LZMA_HEADER_SIZE;
res = lzma_code(&strm, LZMA_RUN);
if(res != LZMA_OK || strm.avail_in != 0) {
lzma_end(&strm);
goto failed;
}
strm.next_in = src + LZMA_HEADER_SIZE;
strm.avail_in = size - LZMA_HEADER_SIZE;
res = lzma_code(&strm, LZMA_FINISH);
lzma_end(&strm);
if(res == LZMA_STREAM_END || (res == LZMA_OK &&
strm.total_out >= uncompressed_size && strm.avail_in == 0))
return uncompressed_size;
failed:
*error = res;
return -1;
}
struct compressor lzma_comp_ops = {
.init = NULL,
.compress = lzma_compress,
.uncompress = lzma_uncompress,
.options = NULL,
.usage = NULL,
.id = LZMA_COMPRESSION,
.name = "lzma",
.supported = 1
};

View file

@ -0,0 +1,365 @@
/*
* Copyright (c) 2013, 2014
* 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.
*
* lzo_wrapper.c
*
* Support for LZO compression http://www.oberhumer.com/opensource/lzo
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <lzo/lzoconf.h>
#include <lzo/lzo1x.h>
#include "squashfs_fs.h"
#include "lzo_wrapper.h"
#include "compressor.h"
static struct lzo_algorithm lzo[] = {
{ "lzo1x_1", LZO1X_1_MEM_COMPRESS, lzo1x_1_compress },
{ "lzo1x_1_11", LZO1X_1_11_MEM_COMPRESS, lzo1x_1_11_compress },
{ "lzo1x_1_12", LZO1X_1_12_MEM_COMPRESS, lzo1x_1_12_compress },
{ "lzo1x_1_15", LZO1X_1_15_MEM_COMPRESS, lzo1x_1_15_compress },
{ "lzo1x_999", LZO1X_999_MEM_COMPRESS, lzo1x_999_wrapper },
{ NULL, 0, NULL }
};
/* default LZO compression algorithm and compression level */
static int algorithm = SQUASHFS_LZO1X_999;
static int compression_level = SQUASHFS_LZO1X_999_COMP_DEFAULT;
/* user specified compression level */
static int user_comp_level = -1;
/*
* This function is called by the options parsing code in mksquashfs.c
* to parse any -X compressor option.
*
* This function returns:
* >=0 (number of additional args parsed) on success
* -1 if the option was unrecognised, or
* -2 if the option was recognised, but otherwise bad in
* some way (e.g. invalid parameter)
*
* Note: this function sets internal compressor state, but does not
* pass back the results of the parsing other than success/failure.
* The lzo_dump_options() function is called later to get the options in
* a format suitable for writing to the filesystem.
*/
static int lzo_options(char *argv[], int argc)
{
(void)argv;
(void)argc;
return 1;
}
/*
* This function is called after all options have been parsed.
* It is used to do post-processing on the compressor options using
* values that were not expected to be known at option parse time.
*
* In this case the LZO algorithm may not be known until after the
* compression level has been set (-Xalgorithm used after -Xcompression-level)
*
* This function returns 0 on successful post processing, or
* -1 on error
*/
static int lzo_options_post(int block_size)
{
/*
* Use of compression level only makes sense for
* LZO1X_999 algorithm
*/
if(user_comp_level != -1) {
if(algorithm != SQUASHFS_LZO1X_999) {
fprintf(stderr, "lzo: -Xcompression-level not "
"supported by selected %s algorithm\n",
lzo[algorithm].name);
fprintf(stderr, "lzo: -Xcompression-level is only "
"applicable for the lzo1x_999 algorithm\n");
goto failed;
}
compression_level = user_comp_level;
}
return 0;
failed:
return -1;
}
/*
* This function is called by mksquashfs to dump the parsed
* compressor options in a format suitable for writing to the
* compressor options field in the filesystem (stored immediately
* after the superblock).
*
* This function returns a pointer to the compression options structure
* to be stored (and the size), or NULL if there are no compression
* options
*
*/
static void *lzo_dump_options(int block_size, int *size)
{
static struct lzo_comp_opts comp_opts;
/*
* If default compression options of SQUASHFS_LZO1X_999 and
* compression level of SQUASHFS_LZO1X_999_COMP_DEFAULT then
* don't store a compression options structure (this is compatible
* with the legacy implementation of LZO for Squashfs)
*/
if(algorithm == SQUASHFS_LZO1X_999 &&
compression_level == SQUASHFS_LZO1X_999_COMP_DEFAULT)
return NULL;
comp_opts.algorithm = algorithm;
comp_opts.compression_level = algorithm == SQUASHFS_LZO1X_999 ?
compression_level : 0;
SQUASHFS_INSWAP_COMP_OPTS(&comp_opts);
*size = sizeof(comp_opts);
return &comp_opts;
}
/*
* This function is a helper specifically for the append mode of
* mksquashfs. Its purpose is to set the internal compressor state
* to the stored compressor options in the passed compressor options
* structure.
*
* In effect this function sets up the compressor options
* to the same state they were when the filesystem was originally
* generated, this is to ensure on appending, the compressor uses
* the same compression options that were used to generate the
* original filesystem.
*
* Note, even if there are no compressor options, this function is still
* called with an empty compressor structure (size == 0), to explicitly
* set the default options, this is to ensure any user supplied
* -X options on the appending mksquashfs command line are over-ridden
*
* This function returns 0 on sucessful extraction of options, and
* -1 on error
*/
static int lzo_extract_options(int block_size, void *buffer, int size)
{
struct lzo_comp_opts *comp_opts = buffer;
if(size == 0) {
/* Set default values */
algorithm = SQUASHFS_LZO1X_999;
compression_level = SQUASHFS_LZO1X_999_COMP_DEFAULT;
return 0;
}
/* we expect a comp_opts structure of sufficient size to be present */
if(size < sizeof(*comp_opts))
goto failed;
SQUASHFS_INSWAP_COMP_OPTS(comp_opts);
/* Check comp_opts structure for correctness */
switch(comp_opts->algorithm) {
case SQUASHFS_LZO1X_1:
case SQUASHFS_LZO1X_1_11:
case SQUASHFS_LZO1X_1_12:
case SQUASHFS_LZO1X_1_15:
if(comp_opts->compression_level != 0) {
fprintf(stderr, "lzo: bad compression level in "
"compression options structure\n");
goto failed;
}
break;
case SQUASHFS_LZO1X_999:
if(comp_opts->compression_level < 1 ||
comp_opts->compression_level > 9) {
fprintf(stderr, "lzo: bad compression level in "
"compression options structure\n");
goto failed;
}
compression_level = comp_opts->compression_level;
break;
default:
fprintf(stderr, "lzo: bad algorithm in compression options "
"structure\n");
goto failed;
}
algorithm = comp_opts->algorithm;
return 0;
failed:
fprintf(stderr, "lzo: error reading stored compressor options from "
"filesystem!\n");
return -1;
}
static void lzo_display_options(void *buffer, int size)
{
struct lzo_comp_opts *comp_opts = buffer;
/* we expect a comp_opts structure of sufficient size to be present */
if(size < sizeof(*comp_opts))
goto failed;
SQUASHFS_INSWAP_COMP_OPTS(comp_opts);
/* Check comp_opts structure for correctness */
switch(comp_opts->algorithm) {
case SQUASHFS_LZO1X_1:
case SQUASHFS_LZO1X_1_11:
case SQUASHFS_LZO1X_1_12:
case SQUASHFS_LZO1X_1_15:
printf("\talgorithm %s\n", lzo[comp_opts->algorithm].name);
break;
case SQUASHFS_LZO1X_999:
if(comp_opts->compression_level < 1 ||
comp_opts->compression_level > 9) {
fprintf(stderr, "lzo: bad compression level in "
"compression options structure\n");
goto failed;
}
printf("\talgorithm %s\n", lzo[comp_opts->algorithm].name);
printf("\tcompression level %d\n",
comp_opts->compression_level);
break;
default:
fprintf(stderr, "lzo: bad algorithm in compression options "
"structure\n");
goto failed;
}
return;
failed:
fprintf(stderr, "lzo: error reading stored compressor options from "
"filesystem!\n");
}
/*
* This function is called by mksquashfs to initialise the
* compressor, before compress() is called.
*
* This function returns 0 on success, and
* -1 on error
*/
static int squashfs_lzo_init(void **strm, int block_size, int datablock)
{
struct lzo_stream *stream;
stream = *strm = malloc(sizeof(struct lzo_stream));
if(stream == NULL)
goto failed;
stream->workspace = malloc(lzo[algorithm].size);
if(stream->workspace == NULL)
goto failed2;
stream->buffer = malloc(LZO_MAX_EXPANSION(block_size));
if(stream->buffer != NULL)
return 0;
free(stream->workspace);
failed2:
free(stream);
failed:
return -1;
}
static int lzo_compress(void *strm, void *dest, void *src, int size,
int block_size, int *error)
{
return 0;
}
static int lzo_uncompress(void *dest, void *src, int size, int outsize,
int *error)
{
int res;
lzo_uint outlen = outsize;
res = lzo1x_decompress_safe(src, size, dest, &outlen, NULL);
if(res != LZO_E_OK) {
*error = res;
return -1;
}
return outlen;
}
static void lzo_usage()
{
int i;
fprintf(stderr, "\t -Xalgorithm <algorithm>\n");
fprintf(stderr, "\t\tWhere <algorithm> is one of:\n");
for(i = 0; lzo[i].name; i++)
fprintf(stderr, "\t\t\t%s%s\n", lzo[i].name,
i == SQUASHFS_LZO1X_999 ? " (default)" : "");
fprintf(stderr, "\t -Xcompression-level <compression-level>\n");
fprintf(stderr, "\t\t<compression-level> should be 1 .. 9 (default "
"%d)\n", SQUASHFS_LZO1X_999_COMP_DEFAULT);
fprintf(stderr, "\t\tOnly applies to lzo1x_999 algorithm\n");
}
/*
* Helper function for lzo1x_999 compression algorithm.
* All other lzo1x_xxx compressors do not take a compression level,
* so we need to wrap lzo1x_999 to pass the compression level which
* is applicable to it
*/
int lzo1x_999_wrapper(const lzo_bytep src, lzo_uint src_len, lzo_bytep dst,
lzo_uintp compsize, lzo_voidp workspace)
{
return lzo1x_999_compress_level(src, src_len, dst, compsize,
workspace, NULL, 0, 0, compression_level);
}
struct compressor lzo_comp_ops = {
.init = squashfs_lzo_init,
.compress = lzo_compress,
.uncompress = lzo_uncompress,
.options = lzo_options,
.options_post = lzo_options_post,
.dump_options = lzo_dump_options,
.extract_options = lzo_extract_options,
.display_options = lzo_display_options,
.usage = lzo_usage,
.id = LZO_COMPRESSION,
.name = "lzo",
.supported = 1
};

View file

@ -0,0 +1,78 @@
#ifndef LZO_WRAPPER_H
#define LZO_WRAPPER_H
/*
* Squashfs
*
* Copyright (c) 2013
* 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.
*
* lzo_wrapper.h
*
*/
#ifndef linux
#define __BYTE_ORDER BYTE_ORDER
#define __BIG_ENDIAN BIG_ENDIAN
#define __LITTLE_ENDIAN LITTLE_ENDIAN
#else
#include <endian.h>
#endif
#if __BYTE_ORDER == __BIG_ENDIAN
extern unsigned int inswap_le32(unsigned int);
#define SQUASHFS_INSWAP_COMP_OPTS(s) { \
(s)->algorithm = inswap_le32((s)->algorithm); \
(s)->compression_level = inswap_le32((s)->compression_level); \
}
#else
#define SQUASHFS_INSWAP_COMP_OPTS(s)
#endif
/* Define the compression flags recognised. */
#define SQUASHFS_LZO1X_1 0
#define SQUASHFS_LZO1X_1_11 1
#define SQUASHFS_LZO1X_1_12 2
#define SQUASHFS_LZO1X_1_15 3
#define SQUASHFS_LZO1X_999 4
/* Default compression level used by SQUASHFS_LZO1X_999 */
#define SQUASHFS_LZO1X_999_COMP_DEFAULT 8
struct lzo_comp_opts {
int algorithm;
int compression_level;
};
struct lzo_algorithm {
char *name;
int size;
int (*compress) (const lzo_bytep, lzo_uint, lzo_bytep, lzo_uintp,
lzo_voidp);
};
struct lzo_stream {
void *workspace;
void *buffer;
};
#define LZO_MAX_EXPANSION(size) (size + (size / 16) + 64 + 3)
int lzo1x_999_wrapper(const lzo_bytep, lzo_uint, lzo_bytep, lzo_uintp,
lzo_voidp);
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,165 @@
#ifndef MKSQUASHFS_H
#define MKSQUASHFS_H
/*
* Squashfs
*
* Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011
* 2012, 2013, 2014, 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.
*
* mksquashfs.h
*
*/
struct dir_info {
char *pathname;
char *subpath;
unsigned int count;
unsigned int directory_count;
int depth;
unsigned int excluded;
char dir_is_ldir;
struct dir_ent *dir_ent;
struct dir_ent *list;
DIR *linuxdir;
};
struct dir_ent {
char *name;
char *source_name;
char *nonstandard_pathname;
struct inode_info *inode;
struct dir_info *dir;
struct dir_info *our_dir;
struct dir_ent *next;
};
struct inode_info {
struct stat buf;
struct inode_info *next;
squashfs_inode inode;
unsigned int inode_number;
unsigned int nlink;
int pseudo_id;
char type;
char read;
char root_entry;
char pseudo_file;
char no_fragments;
char always_use_fragments;
char noD;
char noF;
char symlink[0];
};
/* in memory file info */
struct file_info {
long long file_size;
long long bytes;
long long start;
unsigned int *block_list;
struct file_info *next;
struct fragment *fragment;
unsigned short checksum;
unsigned short fragment_checksum;
char have_frag_checksum;
char have_checksum;
};
/* fragment block data structures */
struct fragment {
unsigned int index;
int offset;
int size;
};
/* in memory uid tables */
#define ID_ENTRIES 256
#define ID_HASH(id) (id & (ID_ENTRIES - 1))
#define ISA_UID 1
#define ISA_GID 2
struct id {
unsigned int id;
int index;
char flags;
struct id *next;
};
/* fragment to file mapping used when appending */
struct append_file {
struct file_info *file;
struct append_file *next;
};
#define PSEUDO_FILE_OTHER 1
#define PSEUDO_FILE_PROCESS 2
#define IS_PSEUDO(a) ((a)->pseudo_file)
#define IS_PSEUDO_PROCESS(a) ((a)->pseudo_file & PSEUDO_FILE_PROCESS)
#define IS_PSEUDO_OTHER(a) ((a)->pseudo_file & PSEUDO_FILE_OTHER)
/*
* Amount of physical memory to use by default, and the default queue
* ratios
*/
#define SQUASHFS_TAKE 4
#define SQUASHFS_READQ_MEM 4
#define SQUASHFS_BWRITEQ_MEM 4
#define SQUASHFS_FWRITEQ_MEM 4
/*
* Lowest amount of physical memory considered viable for Mksquashfs
* to run in Mbytes
*/
#define SQUASHFS_LOWMEM 64
/* offset of data in compressed metadata blocks (allowing room for
* compressed size */
#define BLOCK_OFFSET 2
#ifdef REPRODUCIBLE_DEFAULT
#define NOREP_STR
#define REP_STR " (default)"
#define REP_DEF 1
#else
#define NOREP_STR " (default)"
#define REP_STR
#define REP_DEF 0
#endif
extern struct cache *reader_buffer, *fragment_buffer, *reserve_cache;
struct cache *bwriter_buffer, *fwriter_buffer;
extern struct queue *to_reader, *to_deflate, *to_writer, *from_writer,
*to_frag, *locked_fragment, *to_process_frag;
extern struct append_file **file_mapping;
extern struct seq_queue *to_main, *to_order;
extern pthread_mutex_t fragment_mutex, dup_mutex;
extern struct squashfs_fragment_entry *fragment_table;
extern struct compressor *comp;
extern int block_size;
extern struct file_info *dupl[];
extern int read_fs_bytes(int, long long, int, void *);
extern void add_file(long long, long long, long long, unsigned int *, int,
unsigned int, int, int);
extern struct id *create_id(unsigned int);
extern unsigned int get_uid(unsigned int);
extern unsigned int get_guid(unsigned int);
extern int read_bytes(int, void *, int);
extern unsigned short get_checksum_mem(char *, int);
extern int reproducible;
#endif

View file

@ -0,0 +1,371 @@
/*
* Create a squashfs filesystem. This is a highly compressed read only
* filesystem.
*
* Copyright (c) 2014
* 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.
*
* process_fragments.c
*/
#include <pthread.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <signal.h>
#include <sys/time.h>
#include <string.h>
#include <stdio.h>
#include <math.h>
#include <stdarg.h>
#include <errno.h>
#include <stdlib.h>
#include <dirent.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "caches-queues-lists.h"
#include "squashfs_fs.h"
#include "mksquashfs.h"
#include "error.h"
#include "progressbar.h"
#include "info.h"
#include "compressor.h"
#include "process_fragments.h"
#define FALSE 0
#define TRUE 1
extern struct queue *to_process_frag;
extern struct seq_queue *to_main;
extern int sparse_files;
extern long long start_offset;
/*
* Compute 16 bit BSD checksum over the data, and check for sparseness
*/
static int checksum_sparse(struct file_buffer *file_buffer)
{
unsigned char *b = (unsigned char *) file_buffer->data;
unsigned short chksum = 0;
int bytes = file_buffer->size, sparse = TRUE, value;
while(bytes --) {
chksum = (chksum & 1) ? (chksum >> 1) | 0x8000 : chksum >> 1;
value = *b++;
if(value) {
sparse = FALSE;
chksum += value;
}
}
file_buffer->checksum = chksum;
return sparse;
}
static int read_filesystem(int fd, long long byte, int bytes, void *buff)
{
off_t off = byte;
TRACE("read_filesystem: reading from position 0x%llx, bytes %d\n",
byte, bytes);
if(lseek(fd, start_offset + off, SEEK_SET) == -1) {
ERROR("read_filesystem: Lseek on destination failed because %s, "
"offset=0x%llx\n", strerror(errno), start_offset + off);
return 0;
} else if(read_bytes(fd, buff, bytes) < bytes) {
ERROR("Read on destination failed\n");
return 0;
}
return 1;
}
static struct file_buffer *get_fragment(struct fragment *fragment,
char *data_buffer, int fd)
{
struct squashfs_fragment_entry *disk_fragment;
struct file_buffer *buffer, *compressed_buffer;
long long start_block;
int res, size, index = fragment->index;
char locked;
/*
* Lookup fragment block in cache.
* If the fragment block doesn't exist, then get the compressed version
* from the writer cache or off disk, and decompress it.
*
* This routine has two things which complicate the code:
*
* 1. Multiple threads can simultaneously lookup/create the
* same buffer. This means a buffer needs to be "locked"
* when it is being filled in, to prevent other threads from
* using it when it is not ready. This is because we now do
* fragment duplicate checking in parallel.
* 2. We have two caches which need to be checked for the
* presence of fragment blocks: the normal fragment cache
* and a "reserve" cache. The reserve cache is used to
* prevent an unnecessary pipeline stall when the fragment cache
* is full of fragments waiting to be compressed.
*/
pthread_cleanup_push((void *) pthread_mutex_unlock, &dup_mutex);
pthread_mutex_lock(&dup_mutex);
again:
buffer = cache_lookup_nowait(fragment_buffer, index, &locked);
if(buffer) {
pthread_mutex_unlock(&dup_mutex);
if(locked)
/* got a buffer being filled in. Wait for it */
cache_wait_unlock(buffer);
goto finished;
}
/* not in fragment cache, is it in the reserve cache? */
buffer = cache_lookup_nowait(reserve_cache, index, &locked);
if(buffer) {
pthread_mutex_unlock(&dup_mutex);
if(locked)
/* got a buffer being filled in. Wait for it */
cache_wait_unlock(buffer);
goto finished;
}
/* in neither cache, try to get it from the fragment cache */
buffer = cache_get_nowait(fragment_buffer, index);
if(!buffer) {
/*
* no room, get it from the reserve cache, this is
* dimensioned so it will always have space (no more than
* processors + 1 can have an outstanding reserve buffer)
*/
buffer = cache_get_nowait(reserve_cache, index);
if(!buffer) {
/* failsafe */
ERROR("no space in reserve cache\n");
goto again;
}
}
pthread_mutex_unlock(&dup_mutex);
compressed_buffer = cache_lookup(fwriter_buffer, index);
pthread_cleanup_push((void *) pthread_mutex_unlock, &fragment_mutex);
pthread_mutex_lock(&fragment_mutex);
disk_fragment = &fragment_table[index];
size = SQUASHFS_COMPRESSED_SIZE_BLOCK(disk_fragment->size);
start_block = disk_fragment->start_block;
pthread_cleanup_pop(1);
if(SQUASHFS_COMPRESSED_BLOCK(disk_fragment->size)) {
int error;
char *data;
if(compressed_buffer)
data = compressed_buffer->data;
else {
res = read_filesystem(fd, start_block, size, data_buffer);
if(res == 0) {
ERROR("Failed to read fragment from output"
" filesystem\n");
BAD_ERROR("Output filesystem corrupted?\n");
}
data = data_buffer;
}
res = compressor_uncompress(comp, buffer->data, data, size,
block_size, &error);
if(res == -1)
BAD_ERROR("%s uncompress failed with error code %d\n",
comp->name, error);
} else if(compressed_buffer)
memcpy(buffer->data, compressed_buffer->data, size);
else {
res = read_filesystem(fd, start_block, size, buffer->data);
if(res == 0) {
ERROR("Failed to read fragment from output "
"filesystem\n");
BAD_ERROR("Output filesystem corrupted?\n");
}
}
cache_unlock(buffer);
cache_block_put(compressed_buffer);
finished:
pthread_cleanup_pop(0);
return buffer;
}
struct file_buffer *get_fragment_cksum(struct file_info *file,
char *data_buffer, int fd, unsigned short *checksum)
{
struct file_buffer *frag_buffer;
struct append_file *append;
int index = file->fragment->index;
frag_buffer = get_fragment(file->fragment, data_buffer, fd);
pthread_cleanup_push((void *) pthread_mutex_unlock, &dup_mutex);
for(append = file_mapping[index]; append; append = append->next) {
int offset = append->file->fragment->offset;
int size = append->file->fragment->size;
char *data = frag_buffer->data + offset;
unsigned short cksum = get_checksum_mem(data, size);
if(file == append->file)
*checksum = cksum;
pthread_mutex_lock(&dup_mutex);
append->file->fragment_checksum = cksum;
append->file->have_frag_checksum = TRUE;
pthread_mutex_unlock(&dup_mutex);
}
pthread_cleanup_pop(0);
return frag_buffer;
}
void *frag_thrd(void *destination_file)
{
sigset_t sigmask, old_mask;
char *data_buffer;
int fd;
sigemptyset(&sigmask);
sigaddset(&sigmask, SIGINT);
sigaddset(&sigmask, SIGTERM);
sigaddset(&sigmask, SIGUSR1);
pthread_sigmask(SIG_BLOCK, &sigmask, &old_mask);
fd = open(destination_file, O_RDONLY);
if(fd == -1)
BAD_ERROR("frag_thrd: can't open destination for reading\n");
data_buffer = malloc(SQUASHFS_FILE_MAX_SIZE);
if(data_buffer == NULL)
MEM_ERROR();
pthread_cleanup_push((void *) pthread_mutex_unlock, &dup_mutex);
while(1) {
struct file_buffer *file_buffer = queue_get(to_process_frag);
struct file_buffer *buffer;
int sparse = checksum_sparse(file_buffer);
struct file_info *dupl_ptr;
long long file_size;
unsigned short checksum;
char flag;
int res;
if(sparse_files && sparse) {
file_buffer->c_byte = 0;
file_buffer->fragment = FALSE;
} else
file_buffer->c_byte = file_buffer->size;
/*
* Specutively pull into the fragment cache any fragment blocks
* which contain fragments which *this* fragment may be
* be a duplicate.
*
* By ensuring the fragment block is in cache ahead of time
* should eliminate the parallelisation stall when the
* main thread needs to read the fragment block to do a
* duplicate check on it.
*
* If this is a fragment belonging to a larger file
* (with additional blocks) then ignore it. Here we're
* interested in the "low hanging fruit" of files which
* consist of only a fragment
*/
if(file_buffer->file_size != file_buffer->size) {
seq_queue_put(to_main, file_buffer);
continue;
}
file_size = file_buffer->file_size;
pthread_mutex_lock(&dup_mutex);
dupl_ptr = dupl[DUP_HASH(file_size)];
pthread_mutex_unlock(&dup_mutex);
file_buffer->dupl_start = dupl_ptr;
file_buffer->duplicate = FALSE;
for(; dupl_ptr; dupl_ptr = dupl_ptr->next) {
if(file_size != dupl_ptr->file_size ||
file_size != dupl_ptr->fragment->size)
continue;
pthread_mutex_lock(&dup_mutex);
flag = dupl_ptr->have_frag_checksum;
checksum = dupl_ptr->fragment_checksum;
pthread_mutex_unlock(&dup_mutex);
/*
* If we have the checksum and it matches then
* read in the fragment block.
*
* If we *don't* have the checksum, then we are
* appending, and the fragment block is on the
* "old" filesystem. Read it in and checksum
* the entire fragment buffer
*/
if(!flag) {
buffer = get_fragment_cksum(dupl_ptr,
data_buffer, fd, &checksum);
if(checksum != file_buffer->checksum) {
cache_block_put(buffer);
continue;
}
} else if(checksum == file_buffer->checksum)
buffer = get_fragment(dupl_ptr->fragment,
data_buffer, fd);
else
continue;
res = memcmp(file_buffer->data, buffer->data +
dupl_ptr->fragment->offset, file_size);
cache_block_put(buffer);
if(res == 0) {
struct file_buffer *dup = malloc(sizeof(*dup));
if(dup == NULL)
MEM_ERROR();
memcpy(dup, file_buffer, sizeof(*dup));
cache_block_put(file_buffer);
dup->dupl_start = dupl_ptr;
dup->duplicate = TRUE;
file_buffer = dup;
break;
}
}
seq_queue_put(to_main, file_buffer);
}
pthread_cleanup_pop(0);
}

View file

@ -0,0 +1,30 @@
#ifndef PROCESS_FRAGMENTS_H
#define PROCESS_FRAGMENTS_H
/*
* Create a squashfs filesystem. This is a highly compressed read only
* filesystem.
*
* Copyright (c) 2014
* 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.
*
* process_fragments.h
*/
#define DUP_HASH(a) (a & 0xffff)
extern void *frag_thrd(void *);
#endif

View file

@ -0,0 +1,259 @@
/*
* Create a squashfs filesystem. This is a highly compressed read only
* filesystem.
*
* Copyright (c) 2012, 2013, 2014
* 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.
*
* progressbar.c
*/
#include <pthread.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <signal.h>
#include <sys/time.h>
#include <stdio.h>
#include <math.h>
#include <stdarg.h>
#include <errno.h>
#include <stdlib.h>
#include "error.h"
#define FALSE 0
#define TRUE 1
/* flag whether progressbar display is enabled or not */
int display_progress_bar = FALSE;
/* flag whether the progress bar is temporarily disbled */
int temp_disabled = FALSE;
int rotate = 0;
int cur_uncompressed = 0, estimated_uncompressed = 0;
int columns;
pthread_t progress_thread;
pthread_mutex_t progress_mutex = PTHREAD_MUTEX_INITIALIZER;
static void sigwinch_handler()
{
struct winsize winsize;
if(ioctl(1, TIOCGWINSZ, &winsize) == -1) {
if(isatty(STDOUT_FILENO))
ERROR("TIOCGWINSZ ioctl failed, defaulting to 80 "
"columns\n");
columns = 80;
} else
columns = winsize.ws_col;
}
static void sigalrm_handler()
{
rotate = (rotate + 1) % 4;
}
void inc_progress_bar()
{
cur_uncompressed ++;
}
void dec_progress_bar(int count)
{
cur_uncompressed -= count;
}
void progress_bar_size(int count)
{
estimated_uncompressed += count;
}
static void progress_bar(long long current, long long max, int columns)
{
char rotate_list[] = { '|', '/', '-', '\\' };
int max_digits, used, hashes, spaces;
static int tty = -1;
if(max == 0)
return;
max_digits = floor(log10(max)) + 1;
used = max_digits * 2 + 11;
hashes = (current * (columns - used)) / max;
spaces = columns - used - hashes;
if((current > max) || (columns - used < 0))
return;
if(tty == -1)
tty = isatty(STDOUT_FILENO);
if(!tty) {
static long long previous = -1;
/* Updating much more frequently than this results in huge
* log files. */
if((current % 100) != 0 && current != max)
return;
/* Don't update just to rotate the spinner. */
if(current == previous)
return;
previous = current;
}
printf("\r[");
while (hashes --)
putchar('=');
putchar(rotate_list[rotate]);
while(spaces --)
putchar(' ');
printf("] %*lld/%*lld", max_digits, current, max_digits, max);
printf(" %3lld%%", current * 100 / max);
fflush(stdout);
}
void enable_progress_bar()
{
pthread_cleanup_push((void *) pthread_mutex_unlock, &progress_mutex);
pthread_mutex_lock(&progress_mutex);
if(display_progress_bar)
progress_bar(cur_uncompressed, estimated_uncompressed, columns);
temp_disabled = FALSE;
pthread_cleanup_pop(1);
}
void disable_progress_bar()
{
pthread_cleanup_push((void *) pthread_mutex_unlock, &progress_mutex);
pthread_mutex_lock(&progress_mutex);
if(display_progress_bar)
printf("\n");
temp_disabled = TRUE;
pthread_cleanup_pop(1);
}
void set_progressbar_state(int state)
{
pthread_cleanup_push((void *) pthread_mutex_unlock, &progress_mutex);
pthread_mutex_lock(&progress_mutex);
if(display_progress_bar != state) {
if(display_progress_bar && !temp_disabled) {
progress_bar(cur_uncompressed, estimated_uncompressed,
columns);
printf("\n");
}
display_progress_bar = state;
}
pthread_cleanup_pop(1);
}
void *progress_thrd(void *arg)
{
struct timespec requested_time, remaining;
struct itimerval itimerval;
struct winsize winsize;
if(ioctl(1, TIOCGWINSZ, &winsize) == -1) {
if(isatty(STDOUT_FILENO))
ERROR("TIOCGWINSZ ioctl failed, defaulting to 80 "
"columns\n");
columns = 80;
} else
columns = winsize.ws_col;
signal(SIGWINCH, sigwinch_handler);
signal(SIGALRM, sigalrm_handler);
itimerval.it_value.tv_sec = 0;
itimerval.it_value.tv_usec = 250000;
itimerval.it_interval.tv_sec = 0;
itimerval.it_interval.tv_usec = 250000;
setitimer(ITIMER_REAL, &itimerval, NULL);
requested_time.tv_sec = 0;
requested_time.tv_nsec = 250000000;
while(1) {
int res = nanosleep(&requested_time, &remaining);
if(res == -1 && errno != EINTR)
BAD_ERROR("nanosleep failed in progress thread\n");
pthread_mutex_lock(&progress_mutex);
if(display_progress_bar && !temp_disabled)
progress_bar(cur_uncompressed, estimated_uncompressed,
columns);
pthread_mutex_unlock(&progress_mutex);
}
}
void init_progress_bar()
{
pthread_create(&progress_thread, NULL, progress_thrd, NULL);
}
void progressbar_error(char *fmt, ...)
{
va_list ap;
pthread_cleanup_push((void *) pthread_mutex_unlock, &progress_mutex);
pthread_mutex_lock(&progress_mutex);
if(display_progress_bar && !temp_disabled)
fprintf(stderr, "\n");
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
pthread_cleanup_pop(1);
}
void progressbar_info(char *fmt, ...)
{
va_list ap;
pthread_cleanup_push((void *) pthread_mutex_unlock, &progress_mutex);
pthread_mutex_lock(&progress_mutex);
if(display_progress_bar && !temp_disabled)
printf("\n");
va_start(ap, fmt);
vprintf(fmt, ap);
va_end(ap);
pthread_cleanup_pop(1);
}

View file

@ -0,0 +1,34 @@
#ifndef PROGRESSBAR_H
#define PROGRESSBAR_H
/*
* Create a squashfs filesystem. This is a highly compressed read only
* filesystem.
*
* Copyright (c) 2012, 2013, 2014
* 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.
*
* progressbar.h
*/
extern void inc_progress_bar();
extern void dec_progress_bar(int count);
extern void progress_bar_size(int count);
extern void enable_progress_bar();
extern void disable_progress_bar();
extern void init_progress_bar();
extern void set_progressbar_state(int);
#endif

View file

@ -0,0 +1,559 @@
/*
* Create a squashfs filesystem. This is a highly compressed read only
* filesystem.
*
* Copyright (c) 2009, 2010, 2012, 2014, 2017, 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.
*
* pseudo.c
*/
#include <pwd.h>
#include <grp.h>
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <ctype.h>
#include "pseudo.h"
#include "error.h"
#include "progressbar.h"
#define TRUE 1
#define FALSE 0
extern int read_file(char *filename, char *type, int (parse_line)(char *));
struct pseudo_dev **pseudo_file = NULL;
struct pseudo *pseudo = NULL;
int pseudo_count = 0;
static char *get_component(char *target, char **targname)
{
char *start;
start = target;
while(*target != '/' && *target != '\0')
target ++;
*targname = strndup(start, target - start);
while(*target == '/')
target ++;
return target;
}
/*
* Add pseudo device target to the set of pseudo devices. Pseudo_dev
* describes the pseudo device attributes.
*/
struct pseudo *add_pseudo(struct pseudo *pseudo, struct pseudo_dev *pseudo_dev,
char *target, char *alltarget)
{
char *targname;
int i;
target = get_component(target, &targname);
if(pseudo == NULL) {
pseudo = malloc(sizeof(struct pseudo));
if(pseudo == NULL)
MEM_ERROR();
pseudo->names = 0;
pseudo->count = 0;
pseudo->name = NULL;
}
for(i = 0; i < pseudo->names; i++)
if(strcmp(pseudo->name[i].name, targname) == 0)
break;
if(i == pseudo->names) {
/* allocate new name entry */
pseudo->names ++;
pseudo->name = realloc(pseudo->name, (i + 1) *
sizeof(struct pseudo_entry));
if(pseudo->name == NULL)
MEM_ERROR();
pseudo->name[i].name = targname;
if(target[0] == '\0') {
/* at leaf pathname component */
pseudo->name[i].pseudo = NULL;
pseudo->name[i].pathname = strdup(alltarget);
pseudo->name[i].dev = pseudo_dev;
} else {
/* recurse adding child components */
pseudo->name[i].dev = NULL;
pseudo->name[i].pseudo = add_pseudo(NULL, pseudo_dev,
target, alltarget);
}
} else {
/* existing matching entry */
free(targname);
if(pseudo->name[i].pseudo == NULL) {
/* No sub-directory which means this is the leaf
* component of a pre-existing pseudo file.
*/
if(target[0] != '\0') {
/*
* entry must exist as either a 'd' type or
* 'm' type pseudo file
*/
if(pseudo->name[i].dev->type == 'd' ||
pseudo->name[i].dev->type == 'm')
/* recurse adding child components */
pseudo->name[i].pseudo =
add_pseudo(NULL, pseudo_dev,
target, alltarget);
else {
ERROR_START("%s already exists as a "
"non directory.",
pseudo->name[i].name);
ERROR_EXIT(". Ignoring %s!\n",
alltarget);
}
} else if(memcmp(pseudo_dev, pseudo->name[i].dev,
sizeof(struct pseudo_dev)) != 0) {
ERROR_START("%s already exists as a different "
"pseudo definition.", alltarget);
ERROR_EXIT(" Ignoring!\n");
} else {
ERROR_START("%s already exists as an identical "
"pseudo definition!", alltarget);
ERROR_EXIT(" Ignoring!\n");
}
} else {
if(target[0] == '\0') {
/*
* sub-directory exists, which means we can only
* add a pseudo file of type 'd' or type 'm'
*/
if(pseudo->name[i].dev == NULL &&
(pseudo_dev->type == 'd' ||
pseudo_dev->type == 'm')) {
pseudo->name[i].pathname =
strdup(alltarget);
pseudo->name[i].dev = pseudo_dev;
} else {
ERROR_START("%s already exists as a "
"different pseudo definition.",
pseudo->name[i].name);
ERROR_EXIT(" Ignoring %s!\n",
alltarget);
}
} else
/* recurse adding child components */
add_pseudo(pseudo->name[i].pseudo, pseudo_dev,
target, alltarget);
}
}
return pseudo;
}
/*
* Find subdirectory in pseudo directory referenced by pseudo, matching
* filename. If filename doesn't exist or if filename is a leaf file
* return NULL
*/
struct pseudo *pseudo_subdir(char *filename, struct pseudo *pseudo)
{
int i;
if(pseudo == NULL)
return NULL;
for(i = 0; i < pseudo->names; i++)
if(strcmp(filename, pseudo->name[i].name) == 0)
return pseudo->name[i].pseudo;
return NULL;
}
struct pseudo_entry *pseudo_readdir(struct pseudo *pseudo)
{
if(pseudo == NULL)
return NULL;
while(pseudo->count < pseudo->names) {
if(pseudo->name[pseudo->count].dev != NULL)
return &pseudo->name[pseudo->count++];
else
pseudo->count++;
}
return NULL;
}
int pseudo_exec_file(struct pseudo_dev *dev, int *child)
{
int res, pipefd[2];
res = pipe(pipefd);
if(res == -1) {
ERROR("Executing dynamic pseudo file, pipe failed\n");
return 0;
}
*child = fork();
if(*child == -1) {
ERROR("Executing dynamic pseudo file, fork failed\n");
goto failed;
}
if(*child == 0) {
close(pipefd[0]);
close(STDOUT_FILENO);
res = dup(pipefd[1]);
if(res == -1)
exit(EXIT_FAILURE);
execl("/bin/sh", "sh", "-c", dev->command, (char *) NULL);
exit(EXIT_FAILURE);
}
close(pipefd[1]);
return pipefd[0];
failed:
close(pipefd[0]);
close(pipefd[1]);
return 0;
}
void add_pseudo_file(struct pseudo_dev *dev)
{
pseudo_file = realloc(pseudo_file, (pseudo_count + 1) *
sizeof(struct pseudo_dev *));
if(pseudo_file == NULL)
MEM_ERROR();
dev->pseudo_id = pseudo_count;
pseudo_file[pseudo_count ++] = dev;
}
struct pseudo_dev *get_pseudo_file(int pseudo_id)
{
return pseudo_file[pseudo_id];
}
int read_pseudo_def(char *def)
{
int n, bytes;
int quoted = 0;
unsigned int major = 0, minor = 0, mode;
char type, *ptr;
char suid[100], sgid[100]; /* overflow safe */
char *filename, *name;
char *orig_def = def;
long long uid, gid;
struct pseudo_dev *dev;
/*
* Scan for filename, don't use sscanf() and "%s" because
* that can't handle filenames with spaces.
*
* Filenames with spaces should either escape (backslash) the
* space or use double quotes.
*/
filename = malloc(strlen(def) + 1);
if(filename == NULL)
MEM_ERROR();
for(name = filename; (quoted || !isspace(*def)) && *def != '\0';) {
if(*def == '"') {
quoted = !quoted;
def ++;
continue;
}
if(*def == '\\') {
def ++;
if (*def == '\0')
break;
}
*name ++ = *def ++;
}
*name = '\0';
/* Skip any leading slashes (/) */
for(name = filename; *name == '/'; name ++);
if(*name == '\0') {
ERROR("Not enough or invalid arguments in pseudo file "
"definition \"%s\"\n", orig_def);
goto error;
}
n = sscanf(def, " %c %o %99s %99s %n", &type, &mode, suid, sgid,
&bytes);
def += bytes;
if(n < 4) {
ERROR("Not enough or invalid arguments in pseudo file "
"definition \"%s\"\n", orig_def);
switch(n) {
case -1:
/* FALLTHROUGH */
case 0:
/* FALLTHROUGH */
case 1:
ERROR("Couldn't parse filename, type or octal mode\n");
ERROR("If the filename has spaces, either quote it, or "
"backslash the spaces\n");
break;
case 2:
ERROR("Read filename, type and mode, but failed to "
"read or match uid\n");
break;
default:
ERROR("Read filename, type, mode and uid, but failed "
"to read or match gid\n");
break;
}
goto error;
}
switch(type) {
case 'b':
/* FALLTHROUGH */
case 'c':
n = sscanf(def, "%u %u %n", &major, &minor, &bytes);
def += bytes;
if(n < 2) {
ERROR("Not enough or invalid arguments in %s device "
"pseudo file definition \"%s\"\n", type == 'b' ?
"block" : "character", orig_def);
if(n < 1)
ERROR("Read filename, type, mode, uid and gid, "
"but failed to read or match major\n");
else
ERROR("Read filename, type, mode, uid, gid "
"and major, but failed to read or "
"match minor\n");
goto error;
}
if(major > 0xfff) {
ERROR("Major %d out of range\n", major);
goto error;
}
if(minor > 0xfffff) {
ERROR("Minor %d out of range\n", minor);
goto error;
}
/* FALLTHROUGH */
case 'd':
/* FALLTHROUGH */
case 'm':
/*
* Check for trailing junk after expected arguments
*/
if(def[0] != '\0') {
ERROR("Unexpected tailing characters in pseudo file "
"definition \"%s\"\n", orig_def);
goto error;
}
break;
case 'f':
if(def[0] == '\0') {
ERROR("Not enough arguments in dynamic file pseudo "
"definition \"%s\"\n", orig_def);
ERROR("Expected command, which can be an executable "
"or a piece of shell script\n");
goto error;
}
break;
case 's':
if(def[0] == '\0') {
ERROR("Not enough arguments in symlink pseudo "
"definition \"%s\"\n", orig_def);
ERROR("Expected symlink\n");
goto error;
}
if(strlen(def) > 65535) {
ERROR("Symlink pseudo definition %s is greater than 65535"
" bytes!\n", def);
goto error;
}
break;
default:
ERROR("Unsupported type %c\n", type);
goto error;
}
if(mode > 07777) {
ERROR("Mode %o out of range\n", mode);
goto error;
}
uid = strtoll(suid, &ptr, 10);
if(*ptr == '\0') {
if(uid < 0 || uid > ((1LL << 32) - 1)) {
ERROR("Uid %s out of range\n", suid);
goto error;
}
} else {
struct passwd *pwuid = getpwnam(suid);
if(pwuid)
uid = pwuid->pw_uid;
else {
ERROR("Uid %s invalid uid or unknown user\n", suid);
goto error;
}
}
gid = strtoll(sgid, &ptr, 10);
if(*ptr == '\0') {
if(gid < 0 || gid > ((1LL << 32) - 1)) {
ERROR("Gid %s out of range\n", sgid);
goto error;
}
} else {
struct group *grgid = getgrnam(sgid);
if(grgid)
gid = grgid->gr_gid;
else {
ERROR("Gid %s invalid uid or unknown user\n", sgid);
goto error;
}
}
switch(type) {
case 'b':
mode |= S_IFBLK;
break;
case 'c':
mode |= S_IFCHR;
break;
case 'd':
mode |= S_IFDIR;
break;
case 'f':
mode |= S_IFREG;
break;
case 's':
/* permissions on symlinks are always rwxrwxrwx */
mode = 0777 | S_IFLNK;
break;
}
dev = malloc(sizeof(struct pseudo_dev));
if(dev == NULL)
MEM_ERROR();
dev->type = type;
dev->mode = mode;
dev->uid = uid;
dev->gid = gid;
dev->major = major;
dev->minor = minor;
if(type == 'f') {
dev->command = strdup(def);
add_pseudo_file(dev);
}
if(type == 's')
dev->symlink = strdup(def);
pseudo = add_pseudo(pseudo, dev, name, name);
free(filename);
return TRUE;
error:
ERROR("Pseudo definitions should be of the format\n");
ERROR("\tfilename d mode uid gid\n");
ERROR("\tfilename m mode uid gid\n");
ERROR("\tfilename b mode uid gid major minor\n");
ERROR("\tfilename c mode uid gid major minor\n");
ERROR("\tfilename f mode uid gid command\n");
ERROR("\tfilename s mode uid gid symlink\n");
free(filename);
return FALSE;
}
int read_pseudo_file(char *filename)
{
return read_file(filename, "pseudo", read_pseudo_def);
}
struct pseudo *get_pseudo()
{
return pseudo;
}
#ifdef SQUASHFS_TRACE
static void dump_pseudo(struct pseudo *pseudo, char *string)
{
int i, res;
char *path;
for(i = 0; i < pseudo->names; i++) {
struct pseudo_entry *entry = &pseudo->name[i];
if(string) {
res = asprintf(&path, "%s/%s", string, entry->name);
if(res == -1)
BAD_ERROR("asprintf failed in dump_pseudo\n");
} else
path = entry->name;
if(entry->dev)
ERROR("%s %c 0%o %d %d %d %d\n", path, entry->dev->type,
entry->dev->mode & ~S_IFMT, entry->dev->uid,
entry->dev->gid, entry->dev->major,
entry->dev->minor);
if(entry->pseudo)
dump_pseudo(entry->pseudo, path);
if(string)
free(path);
}
}
void dump_pseudos()
{
if (pseudo)
dump_pseudo(pseudo, NULL);
}
#else
void dump_pseudos()
{
}
#endif

View file

@ -0,0 +1,61 @@
#ifndef PSEUDO_H
#define PSEUDO_H
/*
* Create a squashfs filesystem. This is a highly compressed read only
* filesystem.
*
* Copyright (c) 2009, 2010, 2014
* 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.
*
* pseudo.h
*/
struct pseudo_dev {
char type;
unsigned int mode;
unsigned int uid;
unsigned int gid;
unsigned int major;
unsigned int minor;
int pseudo_id;
union {
char *command;
char *symlink;
};
};
struct pseudo_entry {
char *name;
char *pathname;
struct pseudo *pseudo;
struct pseudo_dev *dev;
};
struct pseudo {
int names;
int count;
struct pseudo_entry *name;
};
extern int read_pseudo_def(char *);
extern int read_pseudo_file(char *);
extern struct pseudo *pseudo_subdir(char *, struct pseudo *);
extern struct pseudo_entry *pseudo_readdir(struct pseudo *);
extern struct pseudo_dev *get_pseudo_file(int);
extern int pseudo_exec_file(struct pseudo_dev *, int *);
extern struct pseudo *get_pseudo();
extern void dump_pseudos();
#endif

View file

@ -0,0 +1,150 @@
/*
* Create a squashfs filesystem. This is a highly compressed read only
* filesystem.
*
* Copyright (c) 2012
* 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_file.c
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include "error.h"
#define TRUE 1
#define FALSE 0
#define MAX_LINE 16384
/*
* Read file, passing each line to parse_line() for
* parsing.
*
* Lines can be split across multiple lines using "\".
*
* Blank lines and comment lines indicated by # are supported.
*/
int read_file(char *filename, char *type, int (parse_line)(char *))
{
FILE *fd;
char *def, *err, *line = NULL;
int res, size = 0;
fd = fopen(filename, "r");
if(fd == NULL) {
ERROR("Could not open %s device file \"%s\" because %s\n",
type, filename, strerror(errno));
return FALSE;
}
while(1) {
int total = 0;
while(1) {
int len;
if(total + (MAX_LINE + 1) > size) {
line = realloc(line, size += (MAX_LINE + 1));
if(line == NULL)
MEM_ERROR();
}
err = fgets(line + total, MAX_LINE + 1, fd);
if(err == NULL)
break;
len = strlen(line + total);
total += len;
if(len == MAX_LINE && line[total - 1] != '\n') {
/* line too large */
ERROR("Line too long when reading "
"%s file \"%s\", larger than "
"%d bytes\n", type, filename, MAX_LINE);
goto failed;
}
/*
* Remove '\n' terminator if it exists (the last line
* in the file may not be '\n' terminated)
*/
if(len && line[total - 1] == '\n') {
line[-- total] = '\0';
len --;
}
/*
* If no line continuation then jump out to
* process line. Note, we have to be careful to
* check for "\\" (backslashed backslash) and to
* ensure we don't look at the previous line
*/
if(len == 0 || line[total - 1] != '\\' || (len >= 2 &&
strcmp(line + total - 2, "\\\\") == 0))
break;
else
total --;
}
if(err == NULL) {
if(ferror(fd)) {
ERROR("Reading %s file \"%s\" failed "
"because %s\n", type, filename,
strerror(errno));
goto failed;
}
/*
* At EOF, normally we'll be finished, but, have to
* check for special case where we had "\" line
* continuation and then hit EOF immediately afterwards
*/
if(total == 0)
break;
else
line[total] = '\0';
}
/* Skip any leading whitespace */
for(def = line; isspace(*def); def ++);
/* if line is now empty after skipping characters, skip it */
if(*def == '\0')
continue;
/* if comment line, skip */
if(*def == '#')
continue;
res = parse_line(def);
if(res == FALSE)
goto failed;
}
fclose(fd);
free(line);
return TRUE;
failed:
fclose(fd);
free(line);
return FALSE;
}

View file

@ -0,0 +1,996 @@
/*
* Read a squashfs filesystem. This is a highly compressed read only
* filesystem.
*
* Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
* 2012, 2013, 2014, 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_fs.c
*/
#define TRUE 1
#define FALSE 0
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <sys/mman.h>
#include <limits.h>
#include <dirent.h>
#ifndef linux
#define __BYTE_ORDER BYTE_ORDER
#define __BIG_ENDIAN BIG_ENDIAN
#define __LITTLE_ENDIAN LITTLE_ENDIAN
#else
#include <endian.h>
#endif
#include <stdlib.h>
#include "squashfs_fs.h"
#include "squashfs_swap.h"
#include "compressor.h"
#include "xattr.h"
#include "error.h"
#include "mksquashfs.h"
int read_block(int fd, long long start, long long *next, int expected,
void *block)
{
unsigned short c_byte;
int res, compressed;
int outlen = expected ? expected : SQUASHFS_METADATA_SIZE;
/* Read block size */
res = read_fs_bytes(fd, start, 2, &c_byte);
if(res == 0)
return 0;
SQUASHFS_INSWAP_SHORTS(&c_byte, 1);
compressed = SQUASHFS_COMPRESSED(c_byte);
c_byte = SQUASHFS_COMPRESSED_SIZE(c_byte);
/*
* The block size should not be larger than
* the uncompressed size (or max uncompressed size if
* expected is 0)
*/
if (c_byte > outlen)
return 0;
if(compressed) {
char buffer[c_byte];
int error;
res = read_fs_bytes(fd, start + 2, c_byte, buffer);
if(res == 0)
return 0;
res = compressor_uncompress(comp, block, buffer, c_byte,
outlen, &error);
if(res == -1) {
ERROR("%s uncompress failed with error code %d\n",
comp->name, error);
return 0;
}
} else {
res = read_fs_bytes(fd, start + 2, c_byte, block);
if(res == 0)
return 0;
res = c_byte;
}
if(next)
*next = start + 2 + c_byte;
/*
* if expected, then check the (uncompressed) return data
* is of the expected size
*/
if(expected && expected != res)
return 0;
else
return res;
}
#define NO_BYTES(SIZE) \
(bytes - (cur_ptr - inode_table) < (SIZE))
#define NO_INODE_BYTES(INODE) NO_BYTES(sizeof(struct INODE))
unsigned char *scan_inode_table(int fd, long long start, long long end,
long long root_inode_start, int root_inode_offset,
struct squashfs_super_block *sBlk, union squashfs_inode_header
*dir_inode, unsigned int *root_inode_block, unsigned int
*root_inode_size, long long *uncompressed_file, unsigned int
*uncompressed_directory, int *file_count, int *sym_count, int
*dev_count, int *dir_count, int *fifo_count, int *sock_count,
unsigned int *id_table)
{
unsigned char *cur_ptr;
unsigned char *inode_table = NULL;
int byte, files = 0;
unsigned int directory_start_block, bytes = 0, size = 0;
struct squashfs_base_inode_header base;
TRACE("scan_inode_table: start 0x%llx, end 0x%llx, root_inode_start "
"0x%llx\n", start, end, root_inode_start);
*root_inode_block = UINT_MAX;
while(start < end) {
if(start == root_inode_start) {
TRACE("scan_inode_table: read compressed block 0x%llx "
"containing root inode\n", start);
*root_inode_block = bytes;
}
if(size - bytes < SQUASHFS_METADATA_SIZE) {
inode_table = realloc(inode_table, size
+= SQUASHFS_METADATA_SIZE);
if(inode_table == NULL)
MEM_ERROR();
}
TRACE("scan_inode_table: reading block 0x%llx\n", start);
byte = read_block(fd, start, &start, 0, inode_table + bytes);
if(byte == 0)
goto corrupted;
bytes += byte;
/* If this is not the last metadata block in the inode table
* 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 && byte != SQUASHFS_METADATA_SIZE)
goto corrupted;
}
/*
* We expect to have found the metadata block containing the
* root inode in the above inode_table metadata block scan. If it
* hasn't been found then the filesystem is corrupted
*/
if(*root_inode_block == UINT_MAX)
goto corrupted;
/*
* The number of bytes available after the root inode medata block
* should be at least the root inode offset + the size of a
* regular directory inode, if not the filesystem is corrupted
*
* +-----------------------+-----------------------+
* | | directory |
* | | inode |
* +-----------------------+-----------------------+
* ^ ^ ^
* *root_inode_block root_inode_offset bytes
*/
if((bytes - *root_inode_block) < (root_inode_offset +
sizeof(struct squashfs_dir_inode_header)))
goto corrupted;
/*
* Read last inode entry which is the root directory inode, and obtain
* the last directory start block index. This is used when calculating
* the total uncompressed directory size. The directory bytes in the
* last * block will be counted as normal.
*
* Note, the previous check ensures the following calculation won't
* underflow, and we won't access beyond the buffer
*/
*root_inode_size = bytes - (*root_inode_block + root_inode_offset);
bytes = *root_inode_block + root_inode_offset;
SQUASHFS_SWAP_DIR_INODE_HEADER(inode_table + bytes, &dir_inode->dir);
if(dir_inode->base.inode_type == SQUASHFS_DIR_TYPE)
directory_start_block = dir_inode->dir.start_block;
else if(dir_inode->base.inode_type == SQUASHFS_LDIR_TYPE) {
if(*root_inode_size < sizeof(struct squashfs_ldir_inode_header))
/* corrupted filesystem */
goto corrupted;
SQUASHFS_SWAP_LDIR_INODE_HEADER(inode_table + bytes,
&dir_inode->ldir);
directory_start_block = dir_inode->ldir.start_block;
} else
/* bad type, corrupted filesystem */
goto corrupted;
get_uid(id_table[dir_inode->base.uid]);
get_guid(id_table[dir_inode->base.guid]);
/* allocate fragment to file mapping table */
file_mapping = calloc(sBlk->fragments, sizeof(struct append_file *));
if(file_mapping == NULL)
MEM_ERROR();
for(cur_ptr = inode_table; cur_ptr < inode_table + bytes; files ++) {
if(NO_INODE_BYTES(squashfs_base_inode_header))
/* corrupted filesystem */
goto corrupted;
SQUASHFS_SWAP_BASE_INODE_HEADER(cur_ptr, &base);
TRACE("scan_inode_table: processing inode @ byte position "
"0x%x, type 0x%x\n",
(unsigned int) (cur_ptr - inode_table),
base.inode_type);
get_uid(id_table[base.uid]);
get_guid(id_table[base.guid]);
switch(base.inode_type) {
case SQUASHFS_FILE_TYPE: {
struct squashfs_reg_inode_header inode;
int frag_bytes, blocks, i;
long long start, file_bytes = 0;
unsigned int *block_list;
if(NO_INODE_BYTES(squashfs_reg_inode_header))
/* corrupted filesystem */
goto corrupted;
SQUASHFS_SWAP_REG_INODE_HEADER(cur_ptr, &inode);
frag_bytes = inode.fragment == SQUASHFS_INVALID_FRAG ?
0 : inode.file_size % sBlk->block_size;
blocks = inode.fragment == SQUASHFS_INVALID_FRAG ?
(inode.file_size + sBlk->block_size - 1) >>
sBlk->block_log : inode.file_size >>
sBlk->block_log;
start = inode.start_block;
TRACE("scan_inode_table: regular file, file_size %d, "
"blocks %d\n", inode.file_size, blocks);
if(NO_BYTES(blocks * sizeof(unsigned int)))
/* corrupted filesystem */
goto corrupted;
block_list = malloc(blocks * sizeof(unsigned int));
if(block_list == NULL)
MEM_ERROR();
cur_ptr += sizeof(inode);
SQUASHFS_SWAP_INTS(cur_ptr, block_list, blocks);
*uncompressed_file += inode.file_size;
(*file_count) ++;
for(i = 0; i < blocks; i++)
file_bytes +=
SQUASHFS_COMPRESSED_SIZE_BLOCK
(block_list[i]);
if(inode.fragment != SQUASHFS_INVALID_FRAG &&
inode.fragment >= sBlk->fragments) {
free(block_list);
goto corrupted;
}
add_file(start, inode.file_size, file_bytes,
block_list, blocks, inode.fragment,
inode.offset, frag_bytes);
cur_ptr += blocks * sizeof(unsigned int);
break;
}
case SQUASHFS_LREG_TYPE: {
struct squashfs_lreg_inode_header inode;
int frag_bytes, blocks, i;
long long start, file_bytes = 0;
unsigned int *block_list;
if(NO_INODE_BYTES(squashfs_lreg_inode_header))
/* corrupted filesystem */
goto corrupted;
SQUASHFS_SWAP_LREG_INODE_HEADER(cur_ptr, &inode);
frag_bytes = inode.fragment == SQUASHFS_INVALID_FRAG ?
0 : inode.file_size % sBlk->block_size;
blocks = inode.fragment == SQUASHFS_INVALID_FRAG ?
(inode.file_size + sBlk->block_size - 1) >>
sBlk->block_log : inode.file_size >>
sBlk->block_log;
start = inode.start_block;
TRACE("scan_inode_table: extended regular "
"file, file_size %lld, blocks %d\n",
inode.file_size, blocks);
if(NO_BYTES(blocks * sizeof(unsigned int)))
/* corrupted filesystem */
goto corrupted;
block_list = malloc(blocks * sizeof(unsigned int));
if(block_list == NULL)
MEM_ERROR();
cur_ptr += sizeof(inode);
SQUASHFS_SWAP_INTS(cur_ptr, block_list, blocks);
*uncompressed_file += inode.file_size;
(*file_count) ++;
for(i = 0; i < blocks; i++)
file_bytes +=
SQUASHFS_COMPRESSED_SIZE_BLOCK
(block_list[i]);
if(inode.fragment != SQUASHFS_INVALID_FRAG &&
inode.fragment >= sBlk->fragments) {
free(block_list);
goto corrupted;
}
add_file(start, inode.file_size, file_bytes,
block_list, blocks, inode.fragment,
inode.offset, frag_bytes);
cur_ptr += blocks * sizeof(unsigned int);
break;
}
case SQUASHFS_SYMLINK_TYPE:
case SQUASHFS_LSYMLINK_TYPE: {
struct squashfs_symlink_inode_header inode;
if(NO_INODE_BYTES(squashfs_symlink_inode_header))
/* corrupted filesystem */
goto corrupted;
SQUASHFS_SWAP_SYMLINK_INODE_HEADER(cur_ptr, &inode);
(*sym_count) ++;
if (inode.inode_type == SQUASHFS_LSYMLINK_TYPE) {
if(NO_BYTES(inode.symlink_size +
sizeof(unsigned int)))
/* corrupted filesystem */
goto corrupted;
cur_ptr += sizeof(inode) + inode.symlink_size +
sizeof(unsigned int);
} else {
if(NO_BYTES(inode.symlink_size))
/* corrupted filesystem */
goto corrupted;
cur_ptr += sizeof(inode) + inode.symlink_size;
}
break;
}
case SQUASHFS_DIR_TYPE: {
struct squashfs_dir_inode_header dir_inode;
if(NO_INODE_BYTES(squashfs_dir_inode_header))
/* corrupted filesystem */
goto corrupted;
SQUASHFS_SWAP_DIR_INODE_HEADER(cur_ptr, &dir_inode);
if(dir_inode.start_block < directory_start_block)
*uncompressed_directory += dir_inode.file_size;
(*dir_count) ++;
cur_ptr += sizeof(struct squashfs_dir_inode_header);
break;
}
case SQUASHFS_LDIR_TYPE: {
struct squashfs_ldir_inode_header dir_inode;
int i;
if(NO_INODE_BYTES(squashfs_ldir_inode_header))
/* corrupted filesystem */
goto corrupted;
SQUASHFS_SWAP_LDIR_INODE_HEADER(cur_ptr, &dir_inode);
if(dir_inode.start_block < directory_start_block)
*uncompressed_directory += dir_inode.file_size;
(*dir_count) ++;
cur_ptr += sizeof(struct squashfs_ldir_inode_header);
for(i = 0; i < dir_inode.i_count; i++) {
struct squashfs_dir_index index;
if(NO_BYTES(sizeof(index)))
/* corrupted filesystem */
goto corrupted;
SQUASHFS_SWAP_DIR_INDEX(cur_ptr, &index);
if(NO_BYTES(index.size + 1))
/* corrupted filesystem */
goto corrupted;
cur_ptr += sizeof(index) + index.size + 1;
}
break;
}
case SQUASHFS_BLKDEV_TYPE:
case SQUASHFS_CHRDEV_TYPE:
if(NO_INODE_BYTES(squashfs_dev_inode_header))
/* corrupted filesystem */
goto corrupted;
(*dev_count) ++;
cur_ptr += sizeof(struct squashfs_dev_inode_header);
break;
case SQUASHFS_LBLKDEV_TYPE:
case SQUASHFS_LCHRDEV_TYPE:
if(NO_INODE_BYTES(squashfs_ldev_inode_header))
/* corrupted filesystem */
goto corrupted;
(*dev_count) ++;
cur_ptr += sizeof(struct squashfs_ldev_inode_header);
break;
case SQUASHFS_FIFO_TYPE:
if(NO_INODE_BYTES(squashfs_ipc_inode_header))
/* corrupted filesystem */
goto corrupted;
(*fifo_count) ++;
cur_ptr += sizeof(struct squashfs_ipc_inode_header);
break;
case SQUASHFS_LFIFO_TYPE:
if(NO_INODE_BYTES(squashfs_lipc_inode_header))
/* corrupted filesystem */
goto corrupted;
(*fifo_count) ++;
cur_ptr += sizeof(struct squashfs_lipc_inode_header);
break;
case SQUASHFS_SOCKET_TYPE:
if(NO_INODE_BYTES(squashfs_ipc_inode_header))
/* corrupted filesystem */
goto corrupted;
(*sock_count) ++;
cur_ptr += sizeof(struct squashfs_ipc_inode_header);
break;
case SQUASHFS_LSOCKET_TYPE:
if(NO_INODE_BYTES(squashfs_lipc_inode_header))
/* corrupted filesystem */
goto corrupted;
(*sock_count) ++;
cur_ptr += sizeof(struct squashfs_lipc_inode_header);
break;
default:
ERROR("Unknown inode type %d in scan_inode_table!\n",
base.inode_type);
goto corrupted;
}
}
printf("Read existing filesystem, %d inodes scanned\n", files);
return inode_table;
corrupted:
ERROR("scan_inode_table: filesystem corruption detected in "
"scanning metadata\n");
free(inode_table);
return NULL;
}
struct compressor *read_super(int fd, struct squashfs_super_block *sBlk, char *source)
{
int res, bytes = 0;
char buffer[SQUASHFS_METADATA_SIZE] __attribute__ ((aligned));
res = read_fs_bytes(fd, SQUASHFS_START, sizeof(struct squashfs_super_block),
sBlk);
if(res == 0) {
ERROR("Can't find a SQUASHFS superblock on %s\n",
source);
ERROR("Wrong filesystem or filesystem is corrupted!\n");
goto failed_mount;
}
SQUASHFS_INSWAP_SUPER_BLOCK(sBlk);
if(sBlk->s_magic != SQUASHFS_MAGIC) {
if(sBlk->s_magic == SQUASHFS_MAGIC_SWAP)
ERROR("Pre 4.0 big-endian filesystem on %s, appending"
" to this is unsupported\n", source);
else {
ERROR("Can't find a SQUASHFS superblock on %s\n",
source);
ERROR("Wrong filesystem or filesystem is corrupted!\n");
}
goto failed_mount;
}
/* Check the MAJOR & MINOR versions */
if(sBlk->s_major != SQUASHFS_MAJOR || sBlk->s_minor > SQUASHFS_MINOR) {
if(sBlk->s_major < 4)
ERROR("Filesystem on %s is a SQUASHFS %d.%d filesystem."
" Appending\nto SQUASHFS %d.%d filesystems is "
"not supported. Please convert it to a "
"SQUASHFS 4 filesystem\n", source,
sBlk->s_major,
sBlk->s_minor, sBlk->s_major, sBlk->s_minor);
else
ERROR("Filesystem on %s is %d.%d, which is a later "
"filesystem version than I support\n",
source, sBlk->s_major, sBlk->s_minor);
goto failed_mount;
}
/* Check the compression type */
comp = lookup_compressor_id(sBlk->compression);
if(!comp->supported) {
ERROR("Filesystem on %s uses %s compression, this is "
"unsupported by this version\n", source, comp->name);
ERROR("Compressors available:\n");
display_compressors("", "");
goto failed_mount;
}
/*
* Read extended superblock information from disk.
*
* Read compressor specific options from disk if present, and pass
* to compressor to set compressor options.
*
* Note, if there's no compressor options present, the compressor
* is still called to set the default options (the defaults may have
* been changed by the user specifying options on the command
* line which need to be over-ridden).
*
* Compressor_extract_options is also used to ensure that
* we know how decompress a filesystem compressed with these
* compression options.
*/
if(SQUASHFS_COMP_OPTS(sBlk->flags)) {
bytes = read_block(fd, sizeof(*sBlk), NULL, 0, buffer);
if(bytes == 0) {
ERROR("Failed to read compressor options from append "
"filesystem\n");
ERROR("Filesystem corrupted?\n");
goto failed_mount;
}
}
res = compressor_extract_options(comp, sBlk->block_size, buffer, bytes);
if(res == -1) {
ERROR("Compressor failed to set compressor options\n");
goto failed_mount;
}
printf("Found a valid %sSQUASHFS superblock on %s.\n",
SQUASHFS_EXPORTABLE(sBlk->flags) ? "exportable " : "", source);
printf("\tCompression used %s\n", comp->name);
printf("\tInodes are %scompressed\n",
SQUASHFS_UNCOMPRESSED_INODES(sBlk->flags) ? "un" : "");
printf("\tData is %scompressed\n",
SQUASHFS_UNCOMPRESSED_DATA(sBlk->flags) ? "un" : "");
printf("\tFragments are %scompressed\n",
SQUASHFS_UNCOMPRESSED_FRAGMENTS(sBlk->flags) ? "un" : "");
printf("\tXattrs are %scompressed\n",
SQUASHFS_UNCOMPRESSED_XATTRS(sBlk->flags) ? "un" : "");
printf("\tFragments are %spresent in the filesystem\n",
SQUASHFS_NO_FRAGMENTS(sBlk->flags) ? "not " : "");
printf("\tAlways-use-fragments option is %sspecified\n",
SQUASHFS_ALWAYS_FRAGMENTS(sBlk->flags) ? "" : "not ");
printf("\tDuplicates are %sremoved\n",
SQUASHFS_DUPLICATES(sBlk->flags) ? "" : "not ");
printf("\tXattrs are %sstored\n",
SQUASHFS_NO_XATTRS(sBlk->flags) ? "not " : "");
printf("\tFilesystem size %.2f Kbytes (%.2f Mbytes)\n",
sBlk->bytes_used / 1024.0, sBlk->bytes_used
/ (1024.0 * 1024.0));
printf("\tBlock size %d\n", sBlk->block_size);
printf("\tNumber of fragments %d\n", sBlk->fragments);
printf("\tNumber of inodes %d\n", sBlk->inodes);
printf("\tNumber of ids %d\n", sBlk->no_ids);
TRACE("sBlk->inode_table_start %llx\n", sBlk->inode_table_start);
TRACE("sBlk->directory_table_start %llx\n",
sBlk->directory_table_start);
TRACE("sBlk->id_table_start %llx\n", sBlk->id_table_start);
TRACE("sBlk->fragment_table_start %llx\n", sBlk->fragment_table_start);
TRACE("sBlk->lookup_table_start %llx\n", sBlk->lookup_table_start);
TRACE("sBlk->xattr_id_table_start %llx\n", sBlk->xattr_id_table_start);
printf("\n");
return comp;
failed_mount:
return NULL;
}
unsigned char *squashfs_readdir(int fd, int root_entries,
unsigned int directory_start_block, int offset, int size,
unsigned int *last_directory_block, struct squashfs_super_block *sBlk,
void (push_directory_entry)(char *, squashfs_inode, int, int))
{
struct squashfs_dir_header dirh;
char buffer[sizeof(struct squashfs_dir_entry) + SQUASHFS_NAME_LEN + 1]
__attribute__ ((aligned));
struct squashfs_dir_entry *dire = (struct squashfs_dir_entry *) buffer;
unsigned char *directory_table = NULL;
int byte, bytes = 0, dir_count;
long long start = sBlk->directory_table_start + directory_start_block,
last_start_block = start;
size += offset;
directory_table = malloc((size + SQUASHFS_METADATA_SIZE * 2 - 1) &
~(SQUASHFS_METADATA_SIZE - 1));
if(directory_table == NULL)
MEM_ERROR();
while(bytes < size) {
int expected = (size - bytes) >= SQUASHFS_METADATA_SIZE ?
SQUASHFS_METADATA_SIZE : 0;
TRACE("squashfs_readdir: reading block 0x%llx, bytes read so "
"far %d\n", start, bytes);
last_start_block = start;
byte = read_block(fd, start, &start, expected, directory_table + bytes);
if(byte == 0) {
ERROR("Failed to read directory\n");
ERROR("Filesystem corrupted?\n");
free(directory_table);
return NULL;
}
bytes += byte;
}
if(!root_entries)
goto all_done;
bytes = offset;
while(bytes < size) {
SQUASHFS_SWAP_DIR_HEADER(directory_table + bytes, &dirh);
dir_count = dirh.count + 1;
/* dir_count should never be larger than SQUASHFS_DIR_COUNT */
if(dir_count > SQUASHFS_DIR_COUNT) {
ERROR("File system corrupted: too many entries in directory\n");
free(directory_table);
return NULL;
}
TRACE("squashfs_readdir: Read directory header @ byte position "
"0x%x, 0x%x directory entries\n", bytes, dir_count);
bytes += sizeof(dirh);
while(dir_count--) {
SQUASHFS_SWAP_DIR_ENTRY(directory_table + bytes, dire);
bytes += sizeof(*dire);
/* size should never be SQUASHFS_NAME_LEN or larger */
if(dire->size >= SQUASHFS_NAME_LEN) {
ERROR("File system corrupted: filename too long\n");
free(directory_table);
return NULL;
}
memcpy(dire->name, directory_table + bytes,
dire->size + 1);
dire->name[dire->size + 1] = '\0';
TRACE("squashfs_readdir: pushing directory entry %s, "
"inode %x:%x, type 0x%x\n", dire->name,
dirh.start_block, dire->offset, dire->type);
push_directory_entry(dire->name,
SQUASHFS_MKINODE(dirh.start_block,
dire->offset), dirh.inode_number +
dire->inode_number, dire->type);
bytes += dire->size + 1;
}
}
all_done:
*last_directory_block = (unsigned int) last_start_block -
sBlk->directory_table_start;
return directory_table;
}
unsigned int *read_id_table(int fd, struct squashfs_super_block *sBlk)
{
int indexes = SQUASHFS_ID_BLOCKS(sBlk->no_ids);
long long index[indexes];
int bytes = SQUASHFS_ID_BYTES(sBlk->no_ids);
unsigned int *id_table;
int res, i;
id_table = malloc(bytes);
if(id_table == NULL)
MEM_ERROR();
res = read_fs_bytes(fd, sBlk->id_table_start,
SQUASHFS_ID_BLOCK_BYTES(sBlk->no_ids), index);
if(res == 0) {
ERROR("Failed to read id table index\n");
ERROR("Filesystem corrupted?\n");
free(id_table);
return NULL;
}
SQUASHFS_INSWAP_ID_BLOCKS(index, indexes);
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 *) id_table) +
(i * SQUASHFS_METADATA_SIZE));
TRACE("Read id table block %d, from 0x%llx, length %d\n", i,
index[i], length);
if(length == 0) {
ERROR("Failed to read id table block %d, from 0x%llx, "
"length %d\n", i, index[i], length);
ERROR("Filesystem corrupted?\n");
free(id_table);
return NULL;
}
}
SQUASHFS_INSWAP_INTS(id_table, sBlk->no_ids);
for(i = 0; i < sBlk->no_ids; i++) {
TRACE("Adding id %d to id tables\n", id_table[i]);
create_id(id_table[i]);
}
return id_table;
}
struct squashfs_fragment_entry *read_fragment_table(int fd, struct squashfs_super_block *sBlk)
{
int res, i;
int bytes = SQUASHFS_FRAGMENT_BYTES(sBlk->fragments);
int indexes = SQUASHFS_FRAGMENT_INDEXES(sBlk->fragments);
long long fragment_table_index[indexes];
struct squashfs_fragment_entry *fragment_table;
TRACE("read_fragment_table: %d fragments, reading %d fragment indexes "
"from 0x%llx\n", sBlk->fragments, indexes,
sBlk->fragment_table_start);
fragment_table = malloc(bytes);
if(fragment_table == NULL)
MEM_ERROR();
res = read_fs_bytes(fd, sBlk->fragment_table_start,
SQUASHFS_FRAGMENT_INDEX_BYTES(sBlk->fragments),
fragment_table_index);
if(res == 0) {
ERROR("Failed to read fragment table index\n");
ERROR("Filesystem corrupted?\n");
free(fragment_table);
return NULL;
}
SQUASHFS_INSWAP_FRAGMENT_INDEXES(fragment_table_index, indexes);
for(i = 0; i < indexes; i++) {
int expected = (i + 1) != indexes ? SQUASHFS_METADATA_SIZE :
bytes & (SQUASHFS_METADATA_SIZE - 1);
int length = read_block(fd, fragment_table_index[i], NULL,
expected, ((unsigned char *) fragment_table) +
(i * SQUASHFS_METADATA_SIZE));
TRACE("Read fragment table block %d, from 0x%llx, length %d\n",
i, fragment_table_index[i], length);
if(length == 0) {
ERROR("Failed to read fragment table block %d, from "
"0x%llx, length %d\n", i,
fragment_table_index[i], length);
ERROR("Filesystem corrupted?\n");
free(fragment_table);
return NULL;
}
}
for(i = 0; i < sBlk->fragments; i++)
SQUASHFS_INSWAP_FRAGMENT_ENTRY(&fragment_table[i]);
return fragment_table;
}
squashfs_inode *read_inode_lookup_table(int fd, struct squashfs_super_block *sBlk)
{
int lookup_bytes = SQUASHFS_LOOKUP_BYTES(sBlk->inodes);
int indexes = SQUASHFS_LOOKUP_BLOCKS(sBlk->inodes);
long long index[indexes];
int res, i;
squashfs_inode *inode_lookup_table;
inode_lookup_table = malloc(lookup_bytes);
if(inode_lookup_table == NULL)
MEM_ERROR();
res = read_fs_bytes(fd, sBlk->lookup_table_start,
SQUASHFS_LOOKUP_BLOCK_BYTES(sBlk->inodes), index);
if(res == 0) {
ERROR("Failed to read inode lookup table index\n");
ERROR("Filesystem corrupted?\n");
free(inode_lookup_table);
return NULL;
}
SQUASHFS_INSWAP_LONG_LONGS(index, indexes);
for(i = 0; i < indexes; i++) {
int expected = (i + 1) != indexes ? SQUASHFS_METADATA_SIZE :
lookup_bytes & (SQUASHFS_METADATA_SIZE - 1);
int length = read_block(fd, index[i], NULL, expected,
((unsigned char *) inode_lookup_table) +
(i * SQUASHFS_METADATA_SIZE));
TRACE("Read inode lookup table block %d, from 0x%llx, length "
"%d\n", i, index[i], length);
if(length == 0) {
ERROR("Failed to read inode lookup table block %d, "
"from 0x%llx, length %d\n", i, index[i],
length);
ERROR("Filesystem corrupted?\n");
free(inode_lookup_table);
return NULL;
}
}
SQUASHFS_INSWAP_LONG_LONGS(inode_lookup_table, sBlk->inodes);
return inode_lookup_table;
}
long long read_filesystem(char *root_name, int fd, struct squashfs_super_block *sBlk,
char **cinode_table, char **data_cache, char **cdirectory_table,
char **directory_data_cache, unsigned int *last_directory_block,
unsigned int *inode_dir_offset, unsigned int *inode_dir_file_size,
unsigned int *root_inode_size, unsigned int *inode_dir_start_block,
int *file_count, int *sym_count, int *dev_count, int *dir_count,
int *fifo_count, int *sock_count, long long *uncompressed_file,
unsigned int *uncompressed_inode, unsigned int *uncompressed_directory,
unsigned int *inode_dir_inode_number,
unsigned int *inode_dir_parent_inode,
void (push_directory_entry)(char *, squashfs_inode, int, int),
struct squashfs_fragment_entry **fragment_table,
squashfs_inode **inode_lookup_table)
{
unsigned char *inode_table = NULL, *directory_table = NULL;
long long start = sBlk->inode_table_start;
long long end = sBlk->directory_table_start;
long long root_inode_start = start +
SQUASHFS_INODE_BLK(sBlk->root_inode);
unsigned int root_inode_offset =
SQUASHFS_INODE_OFFSET(sBlk->root_inode);
unsigned int root_inode_block;
union squashfs_inode_header inode;
unsigned int *id_table = NULL;
int res;
printf("Scanning existing filesystem...\n");
if(get_xattrs(fd, sBlk) == 0)
goto error;
if(sBlk->fragments > 0) {
*fragment_table = read_fragment_table(fd, sBlk);
if(*fragment_table == NULL)
goto error;
}
if(sBlk->lookup_table_start != SQUASHFS_INVALID_BLK) {
*inode_lookup_table = read_inode_lookup_table(fd, sBlk);
if(*inode_lookup_table == NULL)
goto error;
}
id_table = read_id_table(fd, sBlk);
if(id_table == NULL)
goto error;
inode_table = scan_inode_table(fd, start, end, root_inode_start,
root_inode_offset, sBlk, &inode, &root_inode_block,
root_inode_size, uncompressed_file, uncompressed_directory,
file_count, sym_count, dev_count, dir_count, fifo_count,
sock_count, id_table);
if(inode_table == NULL)
goto error;
*uncompressed_inode = root_inode_block;
if(inode.base.inode_type == SQUASHFS_DIR_TYPE ||
inode.base.inode_type == SQUASHFS_LDIR_TYPE) {
if(inode.base.inode_type == SQUASHFS_DIR_TYPE) {
*inode_dir_start_block = inode.dir.start_block;
*inode_dir_offset = inode.dir.offset;
*inode_dir_file_size = inode.dir.file_size - 3;
*inode_dir_inode_number = inode.dir.inode_number;
*inode_dir_parent_inode = inode.dir.parent_inode;
} else {
*inode_dir_start_block = inode.ldir.start_block;
*inode_dir_offset = inode.ldir.offset;
*inode_dir_file_size = inode.ldir.file_size - 3;
*inode_dir_inode_number = inode.ldir.inode_number;
*inode_dir_parent_inode = inode.ldir.parent_inode;
}
directory_table = squashfs_readdir(fd, !root_name,
*inode_dir_start_block, *inode_dir_offset,
*inode_dir_file_size, last_directory_block, sBlk,
push_directory_entry);
if(directory_table == NULL)
goto error;
root_inode_start -= start;
*cinode_table = malloc(root_inode_start);
if(*cinode_table == NULL)
MEM_ERROR();
res = read_fs_bytes(fd, start, root_inode_start, *cinode_table);
if(res == 0) {
ERROR("Failed to read inode table\n");
ERROR("Filesystem corrupted?\n");
goto error;
}
*cdirectory_table = malloc(*last_directory_block);
if(*cdirectory_table == NULL)
MEM_ERROR();
res = read_fs_bytes(fd, sBlk->directory_table_start,
*last_directory_block, *cdirectory_table);
if(res == 0) {
ERROR("Failed to read directory table\n");
ERROR("Filesystem corrupted?\n");
goto error;
}
*data_cache = malloc(root_inode_offset + *root_inode_size);
if(*data_cache == NULL)
MEM_ERROR();
memcpy(*data_cache, inode_table + root_inode_block,
root_inode_offset + *root_inode_size);
*directory_data_cache = malloc(*inode_dir_offset +
*inode_dir_file_size);
if(*directory_data_cache == NULL)
MEM_ERROR();
memcpy(*directory_data_cache, directory_table,
*inode_dir_offset + *inode_dir_file_size);
free(id_table);
free(inode_table);
free(directory_table);
return sBlk->inode_table_start;
}
error:
free(id_table);
free(inode_table);
free(directory_table);
return 0;
}

View file

@ -0,0 +1,34 @@
#ifndef READ_FS_H
#define READ_FS_H
/*
* Squashfs
*
* Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2013
* 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_fs.h
*
*/
extern struct compressor *read_super(int, struct squashfs_super_block *,
char *);
extern long long read_filesystem(char *, int, struct squashfs_super_block *,
char **, char **, char **, char **, unsigned int *, unsigned int *,
unsigned int *, unsigned int *, unsigned int *, int *, int *, int *, int *,
int *, int *, long long *, unsigned int *, unsigned int *, unsigned int *,
unsigned int *, void (push_directory_entry)(char *, squashfs_inode, int, int),
struct squashfs_fragment_entry **, squashfs_inode **);
#endif

View file

@ -0,0 +1,428 @@
/*
* 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;
}

View file

@ -0,0 +1,168 @@
/*
* Create a squashfs filesystem. This is a highly compressed read only
* filesystem.
*
* Copyright (c) 2013, 2014, 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.
*
* restore.c
*/
#include <pthread.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <signal.h>
#include <sys/time.h>
#include <stdio.h>
#include <math.h>
#include <stdarg.h>
#include <errno.h>
#include <stdlib.h>
#include <dirent.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "caches-queues-lists.h"
#include "squashfs_fs.h"
#include "mksquashfs.h"
#include "error.h"
#include "progressbar.h"
#include "info.h"
#define FALSE 0
#define TRUE 1
extern pthread_t reader_thread, writer_thread, main_thread, order_thread;
extern pthread_t *deflator_thread, *frag_deflator_thread, *frag_thread;
extern struct queue *to_deflate, *to_writer, *to_frag, *to_process_frag;
extern struct seq_queue *to_main, *to_order;
extern void restorefs();
extern int processors;
extern int reproducible;
static int interrupted = 0;
static pthread_t restore_thread;
void *restore_thrd(void *arg)
{
sigset_t sigmask, old_mask;
int i, sig;
sigemptyset(&sigmask);
sigaddset(&sigmask, SIGINT);
sigaddset(&sigmask, SIGTERM);
sigaddset(&sigmask, SIGUSR1);
pthread_sigmask(SIG_BLOCK, &sigmask, &old_mask);
while(1) {
sigwait(&sigmask, &sig);
if((sig == SIGINT || sig == SIGTERM) && !interrupted) {
ERROR("Interrupting will restore original "
"filesystem!\n");
ERROR("Interrupt again to quit\n");
interrupted = TRUE;
continue;
}
/* kill main thread/worker threads and restore */
set_progressbar_state(FALSE);
disable_info();
/* first kill the reader thread */
pthread_cancel(reader_thread);
pthread_join(reader_thread, NULL);
/*
* then flush the reader to deflator thread(s) output queue.
* The deflator thread(s) will idle
*/
queue_flush(to_deflate);
/* now kill the deflator thread(s) */
for(i = 0; i < processors; i++)
pthread_cancel(deflator_thread[i]);
for(i = 0; i < processors; i++)
pthread_join(deflator_thread[i], NULL);
/*
* then flush the reader to process fragment thread(s) output
* queue. The process fragment thread(s) will idle
*/
queue_flush(to_process_frag);
/* now kill the process fragment thread(s) */
for(i = 0; i < processors; i++)
pthread_cancel(frag_thread[i]);
for(i = 0; i < processors; i++)
pthread_join(frag_thread[i], NULL);
/*
* then flush the reader/deflator/process fragment to main
* thread output queue. The main thread will idle
*/
seq_queue_flush(to_main);
/* now kill the main thread */
pthread_cancel(main_thread);
pthread_join(main_thread, NULL);
/* then flush the main thread to fragment deflator thread(s)
* queue. The fragment deflator thread(s) will idle
*/
queue_flush(to_frag);
/* now kill the fragment deflator thread(s) */
for(i = 0; i < processors; i++)
pthread_cancel(frag_deflator_thread[i]);
for(i = 0; i < processors; i++)
pthread_join(frag_deflator_thread[i], NULL);
if(reproducible) {
/* then flush the fragment deflator_threads(s)
* to frag orderer thread. The frag orderer
* thread will idle
*/
seq_queue_flush(to_order);
/* now kill the frag orderer thread */
pthread_cancel(order_thread);
pthread_join(order_thread, NULL);
}
/*
* then flush the main thread/fragment deflator thread(s)
* to writer thread queue. The writer thread will idle
*/
queue_flush(to_writer);
/* now kill the writer thread */
pthread_cancel(writer_thread);
pthread_join(writer_thread, NULL);
TRACE("All threads cancelled\n");
restorefs();
}
}
pthread_t *init_restore_thread()
{
pthread_create(&restore_thread, NULL, restore_thrd, NULL);
return &restore_thread;
}

View file

@ -0,0 +1,28 @@
#ifndef RESTORE_H
#define RESTORE_H
/*
* Create a squashfs filesystem. This is a highly compressed read only
* filesystem.
*
* Copyright (c) 2013, 2014
* 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.
*
* restore.h
*/
extern pthread_t *init_restore_thread();
#endif

View file

@ -0,0 +1,363 @@
/*
* Create a squashfs filesystem. This is a highly compressed read only
* filesystem.
*
* Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2012,
* 2013, 2014
* 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.
*
* sort.c
*/
#define TRUE 1
#define FALSE 0
#define MAX_LINE 16384
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <dirent.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include "squashfs_fs.h"
#include "mksquashfs.h"
#include "sort.h"
#include "error.h"
#include "progressbar.h"
int mkisofs_style = -1;
struct sort_info {
dev_t st_dev;
ino_t st_ino;
int priority;
struct sort_info *next;
};
struct sort_info *sort_info_list[65536];
struct priority_entry *priority_list[65536];
extern int silent;
extern void write_file(squashfs_inode *inode, struct dir_ent *dir_ent,
int *c_size);
extern char *pathname(struct dir_ent *dir_ent);
void add_priority_list(struct dir_ent *dir, int priority)
{
struct priority_entry *new_priority_entry;
priority += 32768;
new_priority_entry = malloc(sizeof(struct priority_entry));
if(new_priority_entry == NULL)
MEM_ERROR();
new_priority_entry->dir = dir;;
new_priority_entry->next = priority_list[priority];
priority_list[priority] = new_priority_entry;
}
int get_priority(char *filename, struct stat *buf, int priority)
{
int hash = buf->st_ino & 0xffff;
struct sort_info *s;
for(s = sort_info_list[hash]; s; s = s->next)
if((s->st_dev == buf->st_dev) && (s->st_ino == buf->st_ino)) {
TRACE("returning priority %d (%s)\n", s->priority,
filename);
return s->priority;
}
TRACE("returning priority %d (%s)\n", priority, filename);
return priority;
}
#define ADD_ENTRY(buf, priority) {\
int hash = buf.st_ino & 0xffff;\
struct sort_info *s;\
if((s = malloc(sizeof(struct sort_info))) == NULL) \
MEM_ERROR(); \
s->st_dev = buf.st_dev;\
s->st_ino = buf.st_ino;\
s->priority = priority;\
s->next = sort_info_list[hash];\
sort_info_list[hash] = s;\
}
int add_sort_list(char *path, int priority, int source, char *source_path[])
{
int i, n;
struct stat buf;
TRACE("add_sort_list: filename %s, priority %d\n", path, priority);
if(strlen(path) > 1 && strcmp(path + strlen(path) - 2, "/*") == 0)
path[strlen(path) - 2] = '\0';
TRACE("add_sort_list: filename %s, priority %d\n", path, priority);
re_read:
if(path[0] == '/' || strncmp(path, "./", 2) == 0 ||
strncmp(path, "../", 3) == 0 || mkisofs_style == 1) {
if(lstat(path, &buf) == -1)
goto error;
TRACE("adding filename %s, priority %d, st_dev %d, st_ino "
"%lld\n", path, priority, (int) buf.st_dev,
(long long) buf.st_ino);
ADD_ENTRY(buf, priority);
return TRUE;
}
for(i = 0, n = 0; i < source; i++) {
char *filename;
int res = asprintf(&filename, "%s/%s", source_path[i], path);
if(res == -1)
BAD_ERROR("asprintf failed in add_sort_list\n");
res = lstat(filename, &buf);
free(filename);
if(res == -1) {
if(!(errno == ENOENT || errno == ENOTDIR))
goto error;
continue;
}
ADD_ENTRY(buf, priority);
n ++;
}
if(n == 0 && mkisofs_style == -1 && lstat(path, &buf) != -1) {
ERROR("WARNING: Mkisofs style sortlist detected! This is "
"supported but please\n");
ERROR("convert to mksquashfs style sortlist! A sortlist entry");
ERROR(" should be\neither absolute (starting with ");
ERROR("'/') start with './' or '../' (taken to be\nrelative to "
"$PWD), otherwise it ");
ERROR("is assumed the entry is relative to one\nof the source "
"directories, i.e. with ");
ERROR("\"mksquashfs test test.sqsh\",\nthe sortlist ");
ERROR("entry \"file\" is assumed to be inside the directory "
"test.\n\n");
mkisofs_style = 1;
goto re_read;
}
mkisofs_style = 0;
if(n == 1)
return TRUE;
if(n > 1) {
ERROR(" Ambiguous sortlist entry \"%s\"\n\nIt maps to more "
"than one source entry! Please use an absolute path."
"\n", path);
return FALSE;
}
error:
ERROR_START("Cannot stat sortlist entry \"%s\"\n", path);
ERROR("This is probably because you're using the wrong file\n");
ERROR("path relative to the source directories.");
ERROR_EXIT(" Ignoring");
/*
* Historical note
* Failure to stat a sortlist entry is deliberately ignored, even
* though it is an error. Squashfs release 2.2 changed the behaviour
* to treat it as a fatal error, but it was changed back to
* the original behaviour to ignore it in release 2.2-r2 following
* feedback from users at the time.
*/
return TRUE;
}
void generate_file_priorities(struct dir_info *dir, int priority,
struct stat *buf)
{
struct dir_ent *dir_ent = dir->list;
priority = get_priority(dir->pathname, buf, priority);
for(; dir_ent; dir_ent = dir_ent->next) {
struct stat *buf = &dir_ent->inode->buf;
if(dir_ent->inode->root_entry)
continue;
switch(buf->st_mode & S_IFMT) {
case S_IFREG:
add_priority_list(dir_ent,
get_priority(pathname(dir_ent), buf,
priority));
break;
case S_IFDIR:
generate_file_priorities(dir_ent->dir,
priority, buf);
break;
}
}
}
int read_sort_file(char *filename, int source, char *source_path[])
{
FILE *fd;
char line_buffer[MAX_LINE + 1]; /* overflow safe */
char sort_filename[MAX_LINE + 1]; /* overflow safe */
char *line, *name;
int n, priority, res;
if((fd = fopen(filename, "r")) == NULL) {
ERROR("Failed to open sort file \"%s\" because %s\n",
filename, strerror(errno));
return FALSE;
}
while(fgets(line = line_buffer, MAX_LINE + 1, fd) != NULL) {
int len = strlen(line);
if(len == MAX_LINE && line[len - 1] != '\n') {
/* line too large */
ERROR("Line too long when reading "
"sort file \"%s\", larger than %d "
"bytes\n", filename, MAX_LINE);
goto failed;
}
/*
* Remove '\n' terminator if it exists (the last line
* in the file may not be '\n' terminated)
*/
if(len && line[len - 1] == '\n')
line[len - 1] = '\0';
/* Skip any leading whitespace */
while(isspace(*line))
line ++;
/* if comment line, skip */
if(*line == '#')
continue;
/*
* Scan for filename, don't use sscanf() and "%s" because
* that can't handle filenames with spaces
*/
for(name = sort_filename; !isspace(*line) && *line != '\0';) {
if(*line == '\\') {
line ++;
if (*line == '\0')
break;
}
*name ++ = *line ++;
}
*name = '\0';
/*
* if filename empty, then line was empty of anything but
* whitespace or a backslash character. Skip empy lines
*/
if(sort_filename[0] == '\0')
continue;
/*
* Scan the rest of the line, we expect a decimal number
* which is the filename priority
*/
errno = 0;
res = sscanf(line, "%d%n", &priority, &n);
if((res < 1 || errno) && errno != ERANGE) {
if(errno == 0)
/* No error, assume EOL or match failure */
ERROR("Sort file \"%s\", can't find priority "
"in entry \"%s\", EOL or match "
"failure\n", filename, line_buffer);
else
/* Some other failure not ERANGE */
ERROR("Sscanf failed reading sort file \"%s\" "
"because %s\n", filename,
strerror(errno));
goto failed;
} else if((errno == ERANGE) ||
(priority < -32768 || priority > 32767)) {
ERROR("Sort file \"%s\", entry \"%s\" has priority "
"outside range of -32767:32768.\n", filename,
line_buffer);
goto failed;
}
/* Skip any trailing whitespace */
line += n;
while(isspace(*line))
line ++;
if(*line != '\0') {
ERROR("Sort file \"%s\", trailing characters after "
"priority in entry \"%s\"\n", filename,
line_buffer);
goto failed;
}
res = add_sort_list(sort_filename, priority, source,
source_path);
if(res == FALSE)
goto failed;
}
if(ferror(fd)) {
ERROR("Reading sort file \"%s\" failed because %s\n", filename,
strerror(errno));
goto failed;
}
fclose(fd);
return TRUE;
failed:
fclose(fd);
return FALSE;
}
void sort_files_and_write(struct dir_info *dir)
{
int i;
struct priority_entry *entry;
squashfs_inode inode;
int duplicate_file;
for(i = 65535; i >= 0; i--)
for(entry = priority_list[i]; entry; entry = entry->next) {
TRACE("%d: %s\n", i - 32768, pathname(entry->dir));
if(entry->dir->inode->inode == SQUASHFS_INVALID_BLK) {
write_file(&inode, entry->dir, &duplicate_file);
INFO("file %s, uncompressed size %lld bytes %s"
"\n", pathname(entry->dir),
(long long)
entry->dir->inode->buf.st_size,
duplicate_file ? "DUPLICATE" : "");
entry->dir->inode->inode = inode;
entry->dir->inode->type = SQUASHFS_FILE_TYPE;
} else
INFO("file %s, uncompressed size %lld bytes "
"LINK\n", pathname(entry->dir),
(long long)
entry->dir->inode->buf.st_size);
}
}

View file

@ -0,0 +1,37 @@
#ifndef SORT_H
#define SORT_H
/*
* Squashfs
*
* Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2013
* 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.
*
* sort.h
*/
struct priority_entry {
struct dir_ent *dir;
struct priority_entry *next;
};
extern int read_sort_file(char *, int, char *[]);
extern void sort_files_and_write(struct dir_info *);
extern void generate_file_priorities(struct dir_info *, int priority,
struct stat *);
extern struct priority_entry *priority_list[65536];
#endif

View file

@ -0,0 +1,834 @@
#ifndef SQUASHFS_COMPAT
#define SQUASHFS_COMPAT
/*
* Squashfs
*
* Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2014, 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.
*
* squashfs_compat.h
*/
/*
* definitions for structures on disk - layout 3.x
*/
#define SQUASHFS_CHECK 2
#define SQUASHFS_CHECK_DATA(flags) SQUASHFS_BIT(flags, \
SQUASHFS_CHECK)
/* Max number of uids and gids */
#define SQUASHFS_UIDS 256
#define SQUASHFS_GUIDS 255
struct squashfs_super_block_3 {
unsigned int s_magic;
unsigned int inodes;
unsigned int bytes_used_2;
unsigned int uid_start_2;
unsigned int guid_start_2;
unsigned int inode_table_start_2;
unsigned int directory_table_start_2;
unsigned int s_major:16;
unsigned int s_minor:16;
unsigned int block_size_1:16;
unsigned int block_log:16;
unsigned int flags:8;
unsigned int no_uids:8;
unsigned int no_guids:8;
int mkfs_time /* time of filesystem creation */;
squashfs_inode root_inode;
unsigned int block_size;
unsigned int fragments;
unsigned int fragment_table_start_2;
long long bytes_used;
long long uid_start;
long long guid_start;
long long inode_table_start;
long long directory_table_start;
long long fragment_table_start;
long long lookup_table_start;
} __attribute__ ((packed));
struct squashfs_dir_index_3 {
unsigned int index;
unsigned int start_block;
unsigned char size;
unsigned char name[0];
} __attribute__ ((packed));
struct squashfs_base_inode_header_3 {
unsigned int inode_type:4;
unsigned int mode:12;
unsigned int uid:8;
unsigned int guid:8;
int mtime;
unsigned int inode_number;
} __attribute__ ((packed));
struct squashfs_ipc_inode_header_3 {
unsigned int inode_type:4;
unsigned int mode:12;
unsigned int uid:8;
unsigned int guid:8;
int mtime;
unsigned int inode_number;
unsigned int nlink;
} __attribute__ ((packed));
struct squashfs_dev_inode_header_3 {
unsigned int inode_type:4;
unsigned int mode:12;
unsigned int uid:8;
unsigned int guid:8;
int mtime;
unsigned int inode_number;
unsigned int nlink;
unsigned short rdev;
} __attribute__ ((packed));
struct squashfs_symlink_inode_header_3 {
unsigned int inode_type:4;
unsigned int mode:12;
unsigned int uid:8;
unsigned int guid:8;
int mtime;
unsigned int inode_number;
unsigned int nlink;
unsigned short symlink_size;
char symlink[0];
} __attribute__ ((packed));
struct squashfs_reg_inode_header_3 {
unsigned int inode_type:4;
unsigned int mode:12;
unsigned int uid:8;
unsigned int guid:8;
int mtime;
unsigned int inode_number;
squashfs_block start_block;
unsigned int fragment;
unsigned int offset;
unsigned int file_size;
unsigned short block_list[0];
} __attribute__ ((packed));
struct squashfs_lreg_inode_header_3 {
unsigned int inode_type:4;
unsigned int mode:12;
unsigned int uid:8;
unsigned int guid:8;
int mtime;
unsigned int inode_number;
unsigned int nlink;
squashfs_block start_block;
unsigned int fragment;
unsigned int offset;
long long file_size;
unsigned short block_list[0];
} __attribute__ ((packed));
struct squashfs_dir_inode_header_3 {
unsigned int inode_type:4;
unsigned int mode:12;
unsigned int uid:8;
unsigned int guid:8;
int mtime;
unsigned int inode_number;
unsigned int nlink;
unsigned int file_size:19;
unsigned int offset:13;
unsigned int start_block;
unsigned int parent_inode;
} __attribute__ ((packed));
struct squashfs_ldir_inode_header_3 {
unsigned int inode_type:4;
unsigned int mode:12;
unsigned int uid:8;
unsigned int guid:8;
int mtime;
unsigned int inode_number;
unsigned int nlink;
unsigned int file_size:27;
unsigned int offset:13;
unsigned int start_block;
unsigned int i_count:16;
unsigned int parent_inode;
struct squashfs_dir_index_3 index[0];
} __attribute__ ((packed));
union squashfs_inode_header_3 {
struct squashfs_base_inode_header_3 base;
struct squashfs_dev_inode_header_3 dev;
struct squashfs_symlink_inode_header_3 symlink;
struct squashfs_reg_inode_header_3 reg;
struct squashfs_lreg_inode_header_3 lreg;
struct squashfs_dir_inode_header_3 dir;
struct squashfs_ldir_inode_header_3 ldir;
struct squashfs_ipc_inode_header_3 ipc;
};
struct squashfs_dir_entry_3 {
unsigned int offset:13;
unsigned int type:3;
unsigned int size:8;
int inode_number:16;
char name[0];
} __attribute__ ((packed));
struct squashfs_dir_header_3 {
unsigned int count:8;
unsigned int start_block;
unsigned int inode_number;
} __attribute__ ((packed));
struct squashfs_fragment_entry_3 {
long long start_block;
unsigned int size;
unsigned int pending;
} __attribute__ ((packed));
typedef struct squashfs_super_block_3 squashfs_super_block_3;
typedef struct squashfs_dir_index_3 squashfs_dir_index_3;
typedef struct squashfs_base_inode_header_3 squashfs_base_inode_header_3;
typedef struct squashfs_ipc_inode_header_3 squashfs_ipc_inode_header_3;
typedef struct squashfs_dev_inode_header_3 squashfs_dev_inode_header_3;
typedef struct squashfs_symlink_inode_header_3 squashfs_symlink_inode_header_3;
typedef struct squashfs_reg_inode_header_3 squashfs_reg_inode_header_3;
typedef struct squashfs_lreg_inode_header_3 squashfs_lreg_inode_header_3;
typedef struct squashfs_dir_inode_header_3 squashfs_dir_inode_header_3;
typedef struct squashfs_ldir_inode_header_3 squashfs_ldir_inode_header_3;
typedef struct squashfs_dir_entry_3 squashfs_dir_entry_3;
typedef struct squashfs_dir_header_3 squashfs_dir_header_3;
typedef struct squashfs_fragment_entry_3 squashfs_fragment_entry_3;
/*
* macros to convert each packed bitfield structure from little endian to big
* endian and vice versa. These are needed when creating or using a filesystem
* on a machine with different byte ordering to the target architecture.
*
*/
#define SQUASHFS_SWAP_START \
int bits;\
int b_pos;\
unsigned long long val;\
unsigned char *s;\
unsigned char *d;
#define SQUASHFS_SWAP_SUPER_BLOCK_3(s, d) {\
SQUASHFS_SWAP_START\
SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_super_block_3));\
SQUASHFS_SWAP((s)->s_magic, d, 0, 32);\
SQUASHFS_SWAP((s)->inodes, d, 32, 32);\
SQUASHFS_SWAP((s)->bytes_used_2, d, 64, 32);\
SQUASHFS_SWAP((s)->uid_start_2, d, 96, 32);\
SQUASHFS_SWAP((s)->guid_start_2, d, 128, 32);\
SQUASHFS_SWAP((s)->inode_table_start_2, d, 160, 32);\
SQUASHFS_SWAP((s)->directory_table_start_2, d, 192, 32);\
SQUASHFS_SWAP((s)->s_major, d, 224, 16);\
SQUASHFS_SWAP((s)->s_minor, d, 240, 16);\
SQUASHFS_SWAP((s)->block_size_1, d, 256, 16);\
SQUASHFS_SWAP((s)->block_log, d, 272, 16);\
SQUASHFS_SWAP((s)->flags, d, 288, 8);\
SQUASHFS_SWAP((s)->no_uids, d, 296, 8);\
SQUASHFS_SWAP((s)->no_guids, d, 304, 8);\
SQUASHFS_SWAP((s)->mkfs_time, d, 312, 32);\
SQUASHFS_SWAP((s)->root_inode, d, 344, 64);\
SQUASHFS_SWAP((s)->block_size, d, 408, 32);\
SQUASHFS_SWAP((s)->fragments, d, 440, 32);\
SQUASHFS_SWAP((s)->fragment_table_start_2, d, 472, 32);\
SQUASHFS_SWAP((s)->bytes_used, d, 504, 64);\
SQUASHFS_SWAP((s)->uid_start, d, 568, 64);\
SQUASHFS_SWAP((s)->guid_start, d, 632, 64);\
SQUASHFS_SWAP((s)->inode_table_start, d, 696, 64);\
SQUASHFS_SWAP((s)->directory_table_start, d, 760, 64);\
SQUASHFS_SWAP((s)->fragment_table_start, d, 824, 64);\
SQUASHFS_SWAP((s)->lookup_table_start, d, 888, 64);\
}
#define SQUASHFS_SWAP_BASE_INODE_CORE_3(s, d, n)\
SQUASHFS_MEMSET(s, d, n);\
SQUASHFS_SWAP((s)->inode_type, d, 0, 4);\
SQUASHFS_SWAP((s)->mode, d, 4, 12);\
SQUASHFS_SWAP((s)->uid, d, 16, 8);\
SQUASHFS_SWAP((s)->guid, d, 24, 8);\
SQUASHFS_SWAP((s)->mtime, d, 32, 32);\
SQUASHFS_SWAP((s)->inode_number, d, 64, 32);
#define SQUASHFS_SWAP_BASE_INODE_HEADER_3(s, d, n) {\
SQUASHFS_SWAP_START\
SQUASHFS_SWAP_BASE_INODE_CORE_3(s, d, n)\
}
#define SQUASHFS_SWAP_IPC_INODE_HEADER_3(s, d) {\
SQUASHFS_SWAP_START\
SQUASHFS_SWAP_BASE_INODE_CORE_3(s, d, \
sizeof(struct squashfs_ipc_inode_header_3))\
SQUASHFS_SWAP((s)->nlink, d, 96, 32);\
}
#define SQUASHFS_SWAP_DEV_INODE_HEADER_3(s, d) {\
SQUASHFS_SWAP_START\
SQUASHFS_SWAP_BASE_INODE_CORE_3(s, d, \
sizeof(struct squashfs_dev_inode_header_3)); \
SQUASHFS_SWAP((s)->nlink, d, 96, 32);\
SQUASHFS_SWAP((s)->rdev, d, 128, 16);\
}
#define SQUASHFS_SWAP_SYMLINK_INODE_HEADER_3(s, d) {\
SQUASHFS_SWAP_START\
SQUASHFS_SWAP_BASE_INODE_CORE_3(s, d, \
sizeof(struct squashfs_symlink_inode_header_3));\
SQUASHFS_SWAP((s)->nlink, d, 96, 32);\
SQUASHFS_SWAP((s)->symlink_size, d, 128, 16);\
}
#define SQUASHFS_SWAP_REG_INODE_HEADER_3(s, d) {\
SQUASHFS_SWAP_START\
SQUASHFS_SWAP_BASE_INODE_CORE_3(s, d, \
sizeof(struct squashfs_reg_inode_header_3));\
SQUASHFS_SWAP((s)->start_block, d, 96, 64);\
SQUASHFS_SWAP((s)->fragment, d, 160, 32);\
SQUASHFS_SWAP((s)->offset, d, 192, 32);\
SQUASHFS_SWAP((s)->file_size, d, 224, 32);\
}
#define SQUASHFS_SWAP_LREG_INODE_HEADER_3(s, d) {\
SQUASHFS_SWAP_START\
SQUASHFS_SWAP_BASE_INODE_CORE_3(s, d, \
sizeof(struct squashfs_lreg_inode_header_3));\
SQUASHFS_SWAP((s)->nlink, d, 96, 32);\
SQUASHFS_SWAP((s)->start_block, d, 128, 64);\
SQUASHFS_SWAP((s)->fragment, d, 192, 32);\
SQUASHFS_SWAP((s)->offset, d, 224, 32);\
SQUASHFS_SWAP((s)->file_size, d, 256, 64);\
}
#define SQUASHFS_SWAP_DIR_INODE_HEADER_3(s, d) {\
SQUASHFS_SWAP_START\
SQUASHFS_SWAP_BASE_INODE_CORE_3(s, d, \
sizeof(struct squashfs_dir_inode_header_3));\
SQUASHFS_SWAP((s)->nlink, d, 96, 32);\
SQUASHFS_SWAP((s)->file_size, d, 128, 19);\
SQUASHFS_SWAP((s)->offset, d, 147, 13);\
SQUASHFS_SWAP((s)->start_block, d, 160, 32);\
SQUASHFS_SWAP((s)->parent_inode, d, 192, 32);\
}
#define SQUASHFS_SWAP_LDIR_INODE_HEADER_3(s, d) {\
SQUASHFS_SWAP_START\
SQUASHFS_SWAP_BASE_INODE_CORE_3(s, d, \
sizeof(struct squashfs_ldir_inode_header_3));\
SQUASHFS_SWAP((s)->nlink, d, 96, 32);\
SQUASHFS_SWAP((s)->file_size, d, 128, 27);\
SQUASHFS_SWAP((s)->offset, d, 155, 13);\
SQUASHFS_SWAP((s)->start_block, d, 168, 32);\
SQUASHFS_SWAP((s)->i_count, d, 200, 16);\
SQUASHFS_SWAP((s)->parent_inode, d, 216, 32);\
}
#define SQUASHFS_SWAP_DIR_INDEX_3(s, d) {\
SQUASHFS_SWAP_START\
SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_index_3));\
SQUASHFS_SWAP((s)->index, d, 0, 32);\
SQUASHFS_SWAP((s)->start_block, d, 32, 32);\
SQUASHFS_SWAP((s)->size, d, 64, 8);\
}
#define SQUASHFS_SWAP_DIR_HEADER_3(s, d) {\
SQUASHFS_SWAP_START\
SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_header_3));\
SQUASHFS_SWAP((s)->count, d, 0, 8);\
SQUASHFS_SWAP((s)->start_block, d, 8, 32);\
SQUASHFS_SWAP((s)->inode_number, d, 40, 32);\
}
#define SQUASHFS_SWAP_DIR_ENTRY_3(s, d) {\
SQUASHFS_SWAP_START\
SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_entry_3));\
SQUASHFS_SWAP((s)->offset, d, 0, 13);\
SQUASHFS_SWAP((s)->type, d, 13, 3);\
SQUASHFS_SWAP((s)->size, d, 16, 8);\
SQUASHFS_SWAP((s)->inode_number, d, 24, 16);\
}
#define SQUASHFS_SWAP_INODE_T_3(s, d) SQUASHFS_SWAP_LONG_LONGS_3(s, d, 1)
#define SQUASHFS_SWAP_SHORTS_3(s, d, n) {\
int entry;\
int bit_position;\
SQUASHFS_SWAP_START\
SQUASHFS_MEMSET(s, d, n * 2);\
for(entry = 0, bit_position = 0; entry < n; entry++, bit_position += \
16)\
SQUASHFS_SWAP(s[entry], d, bit_position, 16);\
}
#define SQUASHFS_SWAP_INTS_3(s, d, n) {\
int entry;\
int bit_position;\
SQUASHFS_SWAP_START\
SQUASHFS_MEMSET(s, d, n * 4);\
for(entry = 0, bit_position = 0; entry < n; entry++, bit_position += \
32)\
SQUASHFS_SWAP(s[entry], d, bit_position, 32);\
}
#define SQUASHFS_SWAP_LONG_LONGS_3(s, d, n) {\
int entry;\
int bit_position;\
SQUASHFS_SWAP_START\
SQUASHFS_MEMSET(s, d, n * 8);\
for(entry = 0, bit_position = 0; entry < n; entry++, bit_position += \
64)\
SQUASHFS_SWAP(s[entry], d, bit_position, 64);\
}
#define SQUASHFS_SWAP_DATA(s, d, n, bits) {\
int entry;\
int bit_position;\
SQUASHFS_SWAP_START\
SQUASHFS_MEMSET(s, d, n * bits / 8);\
for(entry = 0, bit_position = 0; entry < n; entry++, bit_position += \
bits)\
SQUASHFS_SWAP(s[entry], d, bit_position, bits);\
}
#define SQUASHFS_SWAP_FRAGMENT_INDEXES_3(s, d, n) SQUASHFS_SWAP_LONG_LONGS_3(s, d, n)
#define SQUASHFS_SWAP_LOOKUP_BLOCKS_3(s, d, n) SQUASHFS_SWAP_LONG_LONGS_3(s, d, n)
#define SQUASHFS_SWAP_FRAGMENT_ENTRY_3(s, d) {\
SQUASHFS_SWAP_START\
SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_fragment_entry_3));\
SQUASHFS_SWAP((s)->start_block, d, 0, 64);\
SQUASHFS_SWAP((s)->size, d, 64, 32);\
}
/* fragment and fragment table defines */
#define SQUASHFS_FRAGMENT_BYTES_3(A) ((A) * sizeof(struct squashfs_fragment_entry_3))
#define SQUASHFS_FRAGMENT_INDEX_3(A) (SQUASHFS_FRAGMENT_BYTES_3(A) / \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_FRAGMENT_INDEX_OFFSET_3(A) (SQUASHFS_FRAGMENT_BYTES_3(A) % \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_FRAGMENT_INDEXES_3(A) ((SQUASHFS_FRAGMENT_BYTES_3(A) + \
SQUASHFS_METADATA_SIZE - 1) / \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_FRAGMENT_INDEX_BYTES_3(A) (SQUASHFS_FRAGMENT_INDEXES_3(A) *\
sizeof(long long))
/* inode lookup table defines */
#define SQUASHFS_LOOKUP_BYTES_3(A) ((A) * sizeof(squashfs_inode))
#define SQUASHFS_LOOKUP_BLOCK_3(A) (SQUASHFS_LOOKUP_BYTES_3(A) / \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_LOOKUP_BLOCK_OFFSET_3(A) (SQUASHFS_LOOKUP_BYTES_3(A) % \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_LOOKUP_BLOCKS_3(A) ((SQUASHFS_LOOKUP_BYTES_3(A) + \
SQUASHFS_METADATA_SIZE - 1) / \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_LOOKUP_BLOCK_BYTES_3(A) (SQUASHFS_LOOKUP_BLOCKS(A) *\
sizeof(long long))
/*
* definitions for structures on disk - layout 1.x
*/
#define SQUASHFS_TYPES 5
#define SQUASHFS_IPC_TYPE 0
struct squashfs_base_inode_header_1 {
unsigned int inode_type:4;
unsigned int mode:12; /* protection */
unsigned int uid:4; /* index into uid table */
unsigned int guid:4; /* index into guid table */
} __attribute__ ((packed));
struct squashfs_ipc_inode_header_1 {
unsigned int inode_type:4;
unsigned int mode:12; /* protection */
unsigned int uid:4; /* index into uid table */
unsigned int guid:4; /* index into guid table */
unsigned int type:4;
unsigned int offset:4;
} __attribute__ ((packed));
struct squashfs_dev_inode_header_1 {
unsigned int inode_type:4;
unsigned int mode:12; /* protection */
unsigned int uid:4; /* index into uid table */
unsigned int guid:4; /* index into guid table */
unsigned short rdev;
} __attribute__ ((packed));
struct squashfs_symlink_inode_header_1 {
unsigned int inode_type:4;
unsigned int mode:12; /* protection */
unsigned int uid:4; /* index into uid table */
unsigned int guid:4; /* index into guid table */
unsigned short symlink_size;
char symlink[0];
} __attribute__ ((packed));
struct squashfs_reg_inode_header_1 {
unsigned int inode_type:4;
unsigned int mode:12; /* protection */
unsigned int uid:4; /* index into uid table */
unsigned int guid:4; /* index into guid table */
int mtime;
unsigned int start_block;
unsigned int file_size:32;
unsigned short block_list[0];
} __attribute__ ((packed));
struct squashfs_dir_inode_header_1 {
unsigned int inode_type:4;
unsigned int mode:12; /* protection */
unsigned int uid:4; /* index into uid table */
unsigned int guid:4; /* index into guid table */
unsigned int file_size:19;
unsigned int offset:13;
int mtime;
unsigned int start_block:24;
} __attribute__ ((packed));
union squashfs_inode_header_1 {
struct squashfs_base_inode_header_1 base;
struct squashfs_dev_inode_header_1 dev;
struct squashfs_symlink_inode_header_1 symlink;
struct squashfs_reg_inode_header_1 reg;
struct squashfs_dir_inode_header_1 dir;
struct squashfs_ipc_inode_header_1 ipc;
};
typedef struct squashfs_dir_index_1 squashfs_dir_index_1;
typedef struct squashfs_base_inode_header_1 squashfs_base_inode_header_1;
typedef struct squashfs_ipc_inode_header_1 squashfs_ipc_inode_header_1;
typedef struct squashfs_dev_inode_header_1 squashfs_dev_inode_header_1;
typedef struct squashfs_symlink_inode_header_1 squashfs_symlink_inode_header_1;
typedef struct squashfs_reg_inode_header_1 squashfs_reg_inode_header_1;
typedef struct squashfs_dir_inode_header_1 squashfs_dir_inode_header_1;
#define SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, n) \
SQUASHFS_MEMSET(s, d, n);\
SQUASHFS_SWAP((s)->inode_type, d, 0, 4);\
SQUASHFS_SWAP((s)->mode, d, 4, 12);\
SQUASHFS_SWAP((s)->uid, d, 16, 4);\
SQUASHFS_SWAP((s)->guid, d, 20, 4);
#define SQUASHFS_SWAP_BASE_INODE_HEADER_1(s, d, n) {\
SQUASHFS_SWAP_START\
SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, n)\
}
#define SQUASHFS_SWAP_IPC_INODE_HEADER_1(s, d) {\
SQUASHFS_SWAP_START\
SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \
sizeof(struct squashfs_ipc_inode_header_1));\
SQUASHFS_SWAP((s)->type, d, 24, 4);\
SQUASHFS_SWAP((s)->offset, d, 28, 4);\
}
#define SQUASHFS_SWAP_DEV_INODE_HEADER_1(s, d) {\
SQUASHFS_SWAP_START\
SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \
sizeof(struct squashfs_dev_inode_header_1));\
SQUASHFS_SWAP((s)->rdev, d, 24, 16);\
}
#define SQUASHFS_SWAP_SYMLINK_INODE_HEADER_1(s, d) {\
SQUASHFS_SWAP_START\
SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \
sizeof(struct squashfs_symlink_inode_header_1));\
SQUASHFS_SWAP((s)->symlink_size, d, 24, 16);\
}
#define SQUASHFS_SWAP_REG_INODE_HEADER_1(s, d) {\
SQUASHFS_SWAP_START\
SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \
sizeof(struct squashfs_reg_inode_header_1));\
SQUASHFS_SWAP((s)->mtime, d, 24, 32);\
SQUASHFS_SWAP((s)->start_block, d, 56, 32);\
SQUASHFS_SWAP((s)->file_size, d, 88, 32);\
}
#define SQUASHFS_SWAP_DIR_INODE_HEADER_1(s, d) {\
SQUASHFS_SWAP_START\
SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \
sizeof(struct squashfs_dir_inode_header_1));\
SQUASHFS_SWAP((s)->file_size, d, 24, 19);\
SQUASHFS_SWAP((s)->offset, d, 43, 13);\
SQUASHFS_SWAP((s)->mtime, d, 56, 32);\
SQUASHFS_SWAP((s)->start_block, d, 88, 24);\
}
/*
* definitions for structures on disk - layout 2.x
*/
struct squashfs_dir_index_2 {
unsigned int index:27;
unsigned int start_block:29;
unsigned char size;
unsigned char name[0];
} __attribute__ ((packed));
struct squashfs_base_inode_header_2 {
unsigned int inode_type:4;
unsigned int mode:12; /* protection */
unsigned int uid:8; /* index into uid table */
unsigned int guid:8; /* index into guid table */
} __attribute__ ((packed));
struct squashfs_ipc_inode_header_2 {
unsigned int inode_type:4;
unsigned int mode:12; /* protection */
unsigned int uid:8; /* index into uid table */
unsigned int guid:8; /* index into guid table */
} __attribute__ ((packed));
struct squashfs_dev_inode_header_2 {
unsigned int inode_type:4;
unsigned int mode:12; /* protection */
unsigned int uid:8; /* index into uid table */
unsigned int guid:8; /* index into guid table */
unsigned short rdev;
} __attribute__ ((packed));
struct squashfs_symlink_inode_header_2 {
unsigned int inode_type:4;
unsigned int mode:12; /* protection */
unsigned int uid:8; /* index into uid table */
unsigned int guid:8; /* index into guid table */
unsigned short symlink_size;
char symlink[0];
} __attribute__ ((packed));
struct squashfs_reg_inode_header_2 {
unsigned int inode_type:4;
unsigned int mode:12; /* protection */
unsigned int uid:8; /* index into uid table */
unsigned int guid:8; /* index into guid table */
int mtime;
unsigned int start_block;
unsigned int fragment;
unsigned int offset;
unsigned int file_size:32;
unsigned short block_list[0];
} __attribute__ ((packed));
struct squashfs_dir_inode_header_2 {
unsigned int inode_type:4;
unsigned int mode:12; /* protection */
unsigned int uid:8; /* index into uid table */
unsigned int guid:8; /* index into guid table */
unsigned int file_size:19;
unsigned int offset:13;
int mtime;
unsigned int start_block:24;
} __attribute__ ((packed));
struct squashfs_ldir_inode_header_2 {
unsigned int inode_type:4;
unsigned int mode:12; /* protection */
unsigned int uid:8; /* index into uid table */
unsigned int guid:8; /* index into guid table */
unsigned int file_size:27;
unsigned int offset:13;
int mtime;
unsigned int start_block:24;
unsigned int i_count:16;
struct squashfs_dir_index_2 index[0];
} __attribute__ ((packed));
union squashfs_inode_header_2 {
struct squashfs_base_inode_header_2 base;
struct squashfs_dev_inode_header_2 dev;
struct squashfs_symlink_inode_header_2 symlink;
struct squashfs_reg_inode_header_2 reg;
struct squashfs_dir_inode_header_2 dir;
struct squashfs_ldir_inode_header_2 ldir;
struct squashfs_ipc_inode_header_2 ipc;
};
struct squashfs_dir_header_2 {
unsigned int count:8;
unsigned int start_block:24;
} __attribute__ ((packed));
struct squashfs_dir_entry_2 {
unsigned int offset:13;
unsigned int type:3;
unsigned int size:8;
char name[0];
} __attribute__ ((packed));
struct squashfs_fragment_entry_2 {
unsigned int start_block;
unsigned int size;
} __attribute__ ((packed));
typedef struct squashfs_dir_index_2 squashfs_dir_index_2;
typedef struct squashfs_base_inode_header_2 squashfs_base_inode_header_2;
typedef struct squashfs_ipc_inode_header_2 squashfs_ipc_inode_header_2;
typedef struct squashfs_dev_inode_header_2 squashfs_dev_inode_header_2;
typedef struct squashfs_symlink_inode_header_2 squashfs_symlink_inode_header_2;
typedef struct squashfs_reg_inode_header_2 squashfs_reg_inode_header_2;
typedef struct squashfs_lreg_inode_header_2 squashfs_lreg_inode_header_2;
typedef struct squashfs_dir_inode_header_2 squashfs_dir_inode_header_2;
typedef struct squashfs_ldir_inode_header_2 squashfs_ldir_inode_header_2;
typedef struct squashfs_dir_entry_2 squashfs_dir_entry_2;
typedef struct squashfs_dir_header_2 squashfs_dir_header_2;
typedef struct squashfs_fragment_entry_2 squashfs_fragment_entry_2;
#define SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, n)\
SQUASHFS_MEMSET(s, d, n);\
SQUASHFS_SWAP((s)->inode_type, d, 0, 4);\
SQUASHFS_SWAP((s)->mode, d, 4, 12);\
SQUASHFS_SWAP((s)->uid, d, 16, 8);\
SQUASHFS_SWAP((s)->guid, d, 24, 8);\
#define SQUASHFS_SWAP_BASE_INODE_HEADER_2(s, d, n) {\
SQUASHFS_SWAP_START\
SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, n)\
}
#define SQUASHFS_SWAP_IPC_INODE_HEADER_2(s, d) \
SQUASHFS_SWAP_BASE_INODE_HEADER_2(s, d, sizeof(struct squashfs_ipc_inode_header_2))
#define SQUASHFS_SWAP_DEV_INODE_HEADER_2(s, d) {\
SQUASHFS_SWAP_START\
SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \
sizeof(struct squashfs_dev_inode_header_2)); \
SQUASHFS_SWAP((s)->rdev, d, 32, 16);\
}
#define SQUASHFS_SWAP_SYMLINK_INODE_HEADER_2(s, d) {\
SQUASHFS_SWAP_START\
SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \
sizeof(struct squashfs_symlink_inode_header_2));\
SQUASHFS_SWAP((s)->symlink_size, d, 32, 16);\
}
#define SQUASHFS_SWAP_REG_INODE_HEADER_2(s, d) {\
SQUASHFS_SWAP_START\
SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \
sizeof(struct squashfs_reg_inode_header_2));\
SQUASHFS_SWAP((s)->mtime, d, 32, 32);\
SQUASHFS_SWAP((s)->start_block, d, 64, 32);\
SQUASHFS_SWAP((s)->fragment, d, 96, 32);\
SQUASHFS_SWAP((s)->offset, d, 128, 32);\
SQUASHFS_SWAP((s)->file_size, d, 160, 32);\
}
#define SQUASHFS_SWAP_DIR_INODE_HEADER_2(s, d) {\
SQUASHFS_SWAP_START\
SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \
sizeof(struct squashfs_dir_inode_header_2));\
SQUASHFS_SWAP((s)->file_size, d, 32, 19);\
SQUASHFS_SWAP((s)->offset, d, 51, 13);\
SQUASHFS_SWAP((s)->mtime, d, 64, 32);\
SQUASHFS_SWAP((s)->start_block, d, 96, 24);\
}
#define SQUASHFS_SWAP_LDIR_INODE_HEADER_2(s, d) {\
SQUASHFS_SWAP_START\
SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \
sizeof(struct squashfs_ldir_inode_header_2));\
SQUASHFS_SWAP((s)->file_size, d, 32, 27);\
SQUASHFS_SWAP((s)->offset, d, 59, 13);\
SQUASHFS_SWAP((s)->mtime, d, 72, 32);\
SQUASHFS_SWAP((s)->start_block, d, 104, 24);\
SQUASHFS_SWAP((s)->i_count, d, 128, 16);\
}
#define SQUASHFS_SWAP_DIR_INDEX_2(s, d) {\
SQUASHFS_SWAP_START\
SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_index_2));\
SQUASHFS_SWAP((s)->index, d, 0, 27);\
SQUASHFS_SWAP((s)->start_block, d, 27, 29);\
SQUASHFS_SWAP((s)->size, d, 56, 8);\
}
#define SQUASHFS_SWAP_DIR_HEADER_2(s, d) {\
SQUASHFS_SWAP_START\
SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_header_2));\
SQUASHFS_SWAP((s)->count, d, 0, 8);\
SQUASHFS_SWAP((s)->start_block, d, 8, 24);\
}
#define SQUASHFS_SWAP_DIR_ENTRY_2(s, d) {\
SQUASHFS_SWAP_START\
SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_entry_2));\
SQUASHFS_SWAP((s)->offset, d, 0, 13);\
SQUASHFS_SWAP((s)->type, d, 13, 3);\
SQUASHFS_SWAP((s)->size, d, 16, 8);\
}
#define SQUASHFS_SWAP_FRAGMENT_ENTRY_2(s, d) {\
SQUASHFS_SWAP_START\
SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_fragment_entry_2));\
SQUASHFS_SWAP((s)->start_block, d, 0, 32);\
SQUASHFS_SWAP((s)->size, d, 32, 32);\
}
#define SQUASHFS_SWAP_FRAGMENT_INDEXES_2(s, d, n) SQUASHFS_SWAP_INTS_3(s, d, n)
/* fragment and fragment table defines */
#define SQUASHFS_FRAGMENT_BYTES_2(A) ((A) * sizeof(struct squashfs_fragment_entry_2))
#define SQUASHFS_FRAGMENT_INDEX_2(A) (SQUASHFS_FRAGMENT_BYTES_2(A) / \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_FRAGMENT_INDEX_OFFSET_2(A) (SQUASHFS_FRAGMENT_BYTES_2(A) % \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_FRAGMENT_INDEXES_2(A) ((SQUASHFS_FRAGMENT_BYTES_2(A) + \
SQUASHFS_METADATA_SIZE - 1) / \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_FRAGMENT_INDEX_BYTES_2(A) (SQUASHFS_FRAGMENT_INDEXES_2(A) *\
sizeof(int))
/*
* macros used to swap each structure entry, taking into account
* bitfields and different bitfield placing conventions on differing architectures
*/
#if __BYTE_ORDER == __BIG_ENDIAN
/* convert from little endian to big endian */
#define SQUASHFS_SWAP(value, p, pos, tbits) _SQUASHFS_SWAP(value, p, pos, tbits, b_pos)
#else
/* convert from big endian to little endian */
#define SQUASHFS_SWAP(value, p, pos, tbits) _SQUASHFS_SWAP(value, p, pos, tbits, 64 - tbits - b_pos)
#endif
#define _SQUASHFS_SWAP(value, p, pos, tbits, SHIFT) {\
b_pos = pos % 8;\
val = 0;\
s = (unsigned char *)p + (pos / 8);\
d = ((unsigned char *) &val) + 7;\
for(bits = 0; bits < (tbits + b_pos); bits += 8) \
*d-- = *s++;\
value = (val >> (SHIFT));\
}
#define SQUASHFS_MEMSET(s, d, n) memset(s, 0, n);
#endif

View file

@ -0,0 +1,499 @@
#ifndef SQUASHFS_FS
#define SQUASHFS_FS
/*
* Squashfs
*
* Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2012,
* 2013, 2014, 2017, 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.
*
* squashfs_fs.h
*/
#define SQUASHFS_CACHED_FRAGMENTS CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE
#define SQUASHFS_MAJOR 4
#define SQUASHFS_MINOR 0
#define SQUASHFS_MAGIC 0x73717368
#define SQUASHFS_MAGIC_SWAP 0x68737173
#define SQUASHFS_START 0
/* size of metadata (inode and directory) blocks */
#define SQUASHFS_METADATA_SIZE 8192
#define SQUASHFS_METADATA_LOG 13
/* default size of data blocks */
#define SQUASHFS_FILE_SIZE 131072
#define SQUASHFS_FILE_MAX_SIZE 1048576
#define SQUASHFS_FILE_MAX_LOG 20
/* Max number of uids and gids */
#define SQUASHFS_IDS 65536
/* Max length of filename (not 255) */
#define SQUASHFS_NAME_LEN 256
/* Max value for directory header count */
#define SQUASHFS_DIR_COUNT 256
#define SQUASHFS_INVALID ((long long) 0xffffffffffff)
#define SQUASHFS_INVALID_FRAG ((unsigned int) 0xffffffff)
#define SQUASHFS_INVALID_XATTR ((unsigned int) 0xffffffff)
#define SQUASHFS_INVALID_BLK ((long long) -1)
#define SQUASHFS_USED_BLK ((long long) -2)
/* Filesystem flags */
#define SQUASHFS_NOI 0
#define SQUASHFS_NOD 1
#define SQUASHFS_CHECK 2
#define SQUASHFS_NOF 3
#define SQUASHFS_NO_FRAG 4
#define SQUASHFS_ALWAYS_FRAG 5
#define SQUASHFS_DUPLICATE 6
#define SQUASHFS_EXPORT 7
#define SQUASHFS_NOX 8
#define SQUASHFS_NO_XATTR 9
#define SQUASHFS_COMP_OPT 10
#define SQUASHFS_NOID 11
#define SQUASHFS_BIT(flag, bit) ((flag >> bit) & 1)
#define SQUASHFS_UNCOMPRESSED_INODES(flags) SQUASHFS_BIT(flags, \
SQUASHFS_NOI)
#define SQUASHFS_UNCOMPRESSED_DATA(flags) SQUASHFS_BIT(flags, \
SQUASHFS_NOD)
#define SQUASHFS_UNCOMPRESSED_FRAGMENTS(flags) SQUASHFS_BIT(flags, \
SQUASHFS_NOF)
#define SQUASHFS_NO_FRAGMENTS(flags) SQUASHFS_BIT(flags, \
SQUASHFS_NO_FRAG)
#define SQUASHFS_ALWAYS_FRAGMENTS(flags) SQUASHFS_BIT(flags, \
SQUASHFS_ALWAYS_FRAG)
#define SQUASHFS_DUPLICATES(flags) SQUASHFS_BIT(flags, \
SQUASHFS_DUPLICATE)
#define SQUASHFS_EXPORTABLE(flags) SQUASHFS_BIT(flags, \
SQUASHFS_EXPORT)
#define SQUASHFS_UNCOMPRESSED_XATTRS(flags) SQUASHFS_BIT(flags, \
SQUASHFS_NOX)
#define SQUASHFS_NO_XATTRS(flags) SQUASHFS_BIT(flags, \
SQUASHFS_NO_XATTR)
#define SQUASHFS_COMP_OPTS(flags) SQUASHFS_BIT(flags, \
SQUASHFS_COMP_OPT)
#define SQUASHFS_UNCOMPRESSED_IDS(flags) SQUASHFS_BIT(flags, \
SQUASHFS_NOID)
#define SQUASHFS_MKFLAGS(noi, nod, nof, nox, noid, no_frag, always_frag, \
duplicate_checking, exportable, no_xattr, comp_opt) (noi | \
(nod << 1) | (nof << 3) | (no_frag << 4) | \
(always_frag << 5) | (duplicate_checking << 6) | \
(exportable << 7) | (nox << 8) | (no_xattr << 9) | \
(comp_opt << 10) | (noid << 11))
/* Max number of types and file types */
#define SQUASHFS_DIR_TYPE 1
#define SQUASHFS_FILE_TYPE 2
#define SQUASHFS_SYMLINK_TYPE 3
#define SQUASHFS_BLKDEV_TYPE 4
#define SQUASHFS_CHRDEV_TYPE 5
#define SQUASHFS_FIFO_TYPE 6
#define SQUASHFS_SOCKET_TYPE 7
#define SQUASHFS_LDIR_TYPE 8
#define SQUASHFS_LREG_TYPE 9
#define SQUASHFS_LSYMLINK_TYPE 10
#define SQUASHFS_LBLKDEV_TYPE 11
#define SQUASHFS_LCHRDEV_TYPE 12
#define SQUASHFS_LFIFO_TYPE 13
#define SQUASHFS_LSOCKET_TYPE 14
/* Xattr types */
#define SQUASHFS_XATTR_USER 0
#define SQUASHFS_XATTR_TRUSTED 1
#define SQUASHFS_XATTR_SECURITY 2
#define SQUASHFS_XATTR_VALUE_OOL 256
#define SQUASHFS_XATTR_PREFIX_MASK 0xff
/* Flag whether block is compressed or uncompressed, bit is set if block is
* uncompressed */
#define SQUASHFS_COMPRESSED_BIT (1 << 15)
#define SQUASHFS_COMPRESSED_SIZE(B) (((B) & ~SQUASHFS_COMPRESSED_BIT) ? \
(B) & ~SQUASHFS_COMPRESSED_BIT : SQUASHFS_COMPRESSED_BIT)
#define SQUASHFS_COMPRESSED(B) (!((B) & SQUASHFS_COMPRESSED_BIT))
#define SQUASHFS_COMPRESSED_BIT_BLOCK (1 << 24)
#define SQUASHFS_COMPRESSED_SIZE_BLOCK(B) ((B) & \
~SQUASHFS_COMPRESSED_BIT_BLOCK)
#define SQUASHFS_COMPRESSED_BLOCK(B) (!((B) & SQUASHFS_COMPRESSED_BIT_BLOCK))
/*
* Inode number ops. Inodes consist of a compressed block number, and an
* uncompressed offset within that block
*/
#define SQUASHFS_INODE_BLK(a) ((unsigned int) ((a) >> 16))
#define SQUASHFS_INODE_OFFSET(a) ((unsigned int) ((a) & 0xffff))
#define SQUASHFS_MKINODE(A, B) ((squashfs_inode)(((squashfs_inode) (A)\
<< 16) + (B)))
/* Compute 32 bit VFS inode number from squashfs inode number */
#define SQUASHFS_MK_VFS_INODE(a, b) ((unsigned int) (((a) << 8) + \
((b) >> 2) + 1))
/* Translate between VFS mode and squashfs mode */
#define SQUASHFS_MODE(a) ((a) & 0xfff)
/* fragment and fragment table defines */
#define SQUASHFS_FRAGMENT_BYTES(A) ((A) * \
sizeof(struct squashfs_fragment_entry))
#define SQUASHFS_FRAGMENT_INDEX(A) (SQUASHFS_FRAGMENT_BYTES(A) / \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_FRAGMENT_INDEX_OFFSET(A) (SQUASHFS_FRAGMENT_BYTES(A) % \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_FRAGMENT_INDEXES(A) ((SQUASHFS_FRAGMENT_BYTES(A) + \
SQUASHFS_METADATA_SIZE - 1) / \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_FRAGMENT_INDEX_BYTES(A) (SQUASHFS_FRAGMENT_INDEXES(A) *\
sizeof(long long))
/* inode lookup table defines */
#define SQUASHFS_LOOKUP_BYTES(A) ((A) * sizeof(squashfs_inode))
#define SQUASHFS_LOOKUP_BLOCK(A) (SQUASHFS_LOOKUP_BYTES(A) / \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_LOOKUP_BLOCK_OFFSET(A) (SQUASHFS_LOOKUP_BYTES(A) % \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_LOOKUP_BLOCKS(A) ((SQUASHFS_LOOKUP_BYTES(A) + \
SQUASHFS_METADATA_SIZE - 1) / \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_LOOKUP_BLOCK_BYTES(A) (SQUASHFS_LOOKUP_BLOCKS(A) *\
sizeof(long long))
/* uid lookup table defines */
#define SQUASHFS_ID_BYTES(A) ((A) * sizeof(unsigned int))
#define SQUASHFS_ID_BLOCK(A) (SQUASHFS_ID_BYTES(A) / \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_ID_BLOCK_OFFSET(A) (SQUASHFS_ID_BYTES(A) % \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_ID_BLOCKS(A) ((SQUASHFS_ID_BYTES(A) + \
SQUASHFS_METADATA_SIZE - 1) / \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_ID_BLOCK_BYTES(A) (SQUASHFS_ID_BLOCKS(A) *\
sizeof(long long))
/* xattr id lookup table defines */
#define SQUASHFS_XATTR_BYTES(A) ((A) * sizeof(struct squashfs_xattr_id))
#define SQUASHFS_XATTR_BLOCK(A) (SQUASHFS_XATTR_BYTES(A) / \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_XATTR_BLOCK_OFFSET(A) (SQUASHFS_XATTR_BYTES(A) % \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_XATTR_BLOCKS(A) ((SQUASHFS_XATTR_BYTES(A) + \
SQUASHFS_METADATA_SIZE - 1) / \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_XATTR_BLOCK_BYTES(A) (SQUASHFS_XATTR_BLOCKS(A) *\
sizeof(long long))
#define SQUASHFS_XATTR_BLK(A) ((unsigned int) ((A) >> 16))
#define SQUASHFS_XATTR_OFFSET(A) ((unsigned int) ((A) & 0xffff))
/* cached data constants for filesystem */
#define SQUASHFS_CACHED_BLKS 8
#define SQUASHFS_MAX_FILE_SIZE_LOG 64
#define SQUASHFS_MAX_FILE_SIZE ((long long) 1 << \
(SQUASHFS_MAX_FILE_SIZE_LOG - 2))
#define SQUASHFS_MARKER_BYTE 0xff
/* meta index cache */
#define SQUASHFS_META_INDEXES (SQUASHFS_METADATA_SIZE / sizeof(unsigned int))
#define SQUASHFS_META_ENTRIES 31
#define SQUASHFS_META_NUMBER 8
#define SQUASHFS_SLOTS 4
struct meta_entry {
long long data_block;
unsigned int index_block;
unsigned short offset;
unsigned short pad;
};
struct meta_index {
unsigned int inode_number;
unsigned int offset;
unsigned short entries;
unsigned short skip;
unsigned short locked;
unsigned short pad;
struct meta_entry meta_entry[SQUASHFS_META_ENTRIES];
};
/*
* definitions for structures on disk
*/
typedef long long squashfs_block;
typedef long long squashfs_inode;
#define ZLIB_COMPRESSION 1
#define LZMA_COMPRESSION 2
#define LZO_COMPRESSION 3
#define XZ_COMPRESSION 4
#define LZ4_COMPRESSION 5
#define ZSTD_COMPRESSION 6
struct squashfs_super_block {
unsigned int s_magic;
unsigned int inodes;
unsigned int mkfs_time /* time of filesystem creation */;
unsigned int block_size;
unsigned int fragments;
unsigned short compression;
unsigned short block_log;
unsigned short flags;
unsigned short no_ids;
unsigned short s_major;
unsigned short s_minor;
squashfs_inode root_inode;
long long bytes_used;
long long id_table_start;
long long xattr_id_table_start;
long long inode_table_start;
long long directory_table_start;
long long fragment_table_start;
long long lookup_table_start;
};
struct squashfs_dir_index {
unsigned int index;
unsigned int start_block;
unsigned int size;
unsigned char name[0];
};
struct squashfs_base_inode_header {
unsigned short inode_type;
unsigned short mode;
unsigned short uid;
unsigned short guid;
unsigned int mtime;
unsigned int inode_number;
};
struct squashfs_ipc_inode_header {
unsigned short inode_type;
unsigned short mode;
unsigned short uid;
unsigned short guid;
unsigned int mtime;
unsigned int inode_number;
unsigned int nlink;
};
struct squashfs_lipc_inode_header {
unsigned short inode_type;
unsigned short mode;
unsigned short uid;
unsigned short guid;
unsigned int mtime;
unsigned int inode_number;
unsigned int nlink;
unsigned int xattr;
};
struct squashfs_dev_inode_header {
unsigned short inode_type;
unsigned short mode;
unsigned short uid;
unsigned short guid;
unsigned int mtime;
unsigned int inode_number;
unsigned int nlink;
unsigned int rdev;
};
struct squashfs_ldev_inode_header {
unsigned short inode_type;
unsigned short mode;
unsigned short uid;
unsigned short guid;
unsigned int mtime;
unsigned int inode_number;
unsigned int nlink;
unsigned int rdev;
unsigned int xattr;
};
struct squashfs_symlink_inode_header {
unsigned short inode_type;
unsigned short mode;
unsigned short uid;
unsigned short guid;
unsigned int mtime;
unsigned int inode_number;
unsigned int nlink;
unsigned int symlink_size;
char symlink[0];
};
struct squashfs_reg_inode_header {
unsigned short inode_type;
unsigned short mode;
unsigned short uid;
unsigned short guid;
unsigned int mtime;
unsigned int inode_number;
unsigned int start_block;
unsigned int fragment;
unsigned int offset;
unsigned int file_size;
unsigned int block_list[0];
};
struct squashfs_lreg_inode_header {
unsigned short inode_type;
unsigned short mode;
unsigned short uid;
unsigned short guid;
unsigned int mtime;
unsigned int inode_number;
squashfs_block start_block;
long long file_size;
long long sparse;
unsigned int nlink;
unsigned int fragment;
unsigned int offset;
unsigned int xattr;
unsigned int block_list[0];
};
struct squashfs_dir_inode_header {
unsigned short inode_type;
unsigned short mode;
unsigned short uid;
unsigned short guid;
unsigned int mtime;
unsigned int inode_number;
unsigned int start_block;
unsigned int nlink;
unsigned short file_size;
unsigned short offset;
unsigned int parent_inode;
};
struct squashfs_ldir_inode_header {
unsigned short inode_type;
unsigned short mode;
unsigned short uid;
unsigned short guid;
unsigned int mtime;
unsigned int inode_number;
unsigned int nlink;
unsigned int file_size;
unsigned int start_block;
unsigned int parent_inode;
unsigned short i_count;
unsigned short offset;
unsigned int xattr;
struct squashfs_dir_index index[0];
};
union squashfs_inode_header {
struct squashfs_base_inode_header base;
struct squashfs_dev_inode_header dev;
struct squashfs_ldev_inode_header ldev;
struct squashfs_symlink_inode_header symlink;
struct squashfs_reg_inode_header reg;
struct squashfs_lreg_inode_header lreg;
struct squashfs_dir_inode_header dir;
struct squashfs_ldir_inode_header ldir;
struct squashfs_ipc_inode_header ipc;
struct squashfs_lipc_inode_header lipc;
};
struct squashfs_dir_entry {
unsigned short offset;
short inode_number;
unsigned short type;
unsigned short size;
char name[0];
};
struct squashfs_dir_header {
unsigned int count;
unsigned int start_block;
unsigned int inode_number;
};
struct squashfs_fragment_entry {
long long start_block;
unsigned int size;
unsigned int unused;
};
struct squashfs_xattr_entry {
unsigned short type;
unsigned short size;
};
struct squashfs_xattr_val {
unsigned int vsize;
};
struct squashfs_xattr_id {
long long xattr;
unsigned int count;
unsigned int size;
};
struct squashfs_xattr_table {
long long xattr_table_start;
unsigned int xattr_ids;
unsigned int unused;
};
#endif

View file

@ -0,0 +1,423 @@
#ifndef SQUASHFS_SWAP_H
#define SQUASHFS_SWAP_H
/*
* Squashfs
*
* Copyright (c) 2008, 2009, 2010, 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.
*
* squashfs_swap.h
*/
/*
* macros to convert each stucture from big endian to little endian
*/
#if __BYTE_ORDER == __BIG_ENDIAN
#include <stddef.h>
extern void swap_le16(void *, void *);
extern void swap_le32(void *, void *);
extern void swap_le64(void *, void *);
extern void swap_le16_num(void *, void *, int);
extern void swap_le32_num(void *, void *, int);
extern void swap_le64_num(void *, void *, int);
extern unsigned short inswap_le16(unsigned short);
extern unsigned int inswap_le32(unsigned int);
extern long long inswap_le64(long long);
extern void inswap_le16_num(unsigned short *, int);
extern void inswap_le32_num(unsigned int *, int);
extern void inswap_le64_num(long long *, int);
#define _SQUASHFS_SWAP_SUPER_BLOCK(s, d, SWAP_FUNC) {\
SWAP_FUNC(32, s, d, s_magic, struct squashfs_super_block);\
SWAP_FUNC(32, s, d, inodes, struct squashfs_super_block);\
SWAP_FUNC(32, s, d, mkfs_time, struct squashfs_super_block);\
SWAP_FUNC(32, s, d, block_size, struct squashfs_super_block);\
SWAP_FUNC(32, s, d, fragments, struct squashfs_super_block);\
SWAP_FUNC(16, s, d, compression, struct squashfs_super_block);\
SWAP_FUNC(16, s, d, block_log, struct squashfs_super_block);\
SWAP_FUNC(16, s, d, flags, struct squashfs_super_block);\
SWAP_FUNC(16, s, d, no_ids, struct squashfs_super_block);\
SWAP_FUNC(16, s, d, s_major, struct squashfs_super_block);\
SWAP_FUNC(16, s, d, s_minor, struct squashfs_super_block);\
SWAP_FUNC(64, s, d, root_inode, struct squashfs_super_block);\
SWAP_FUNC(64, s, d, bytes_used, struct squashfs_super_block);\
SWAP_FUNC(64, s, d, id_table_start, struct squashfs_super_block);\
SWAP_FUNC(64, s, d, xattr_id_table_start, struct squashfs_super_block);\
SWAP_FUNC(64, s, d, inode_table_start, struct squashfs_super_block);\
SWAP_FUNC(64, s, d, directory_table_start, struct squashfs_super_block);\
SWAP_FUNC(64, s, d, fragment_table_start, struct squashfs_super_block);\
SWAP_FUNC(64, s, d, lookup_table_start, struct squashfs_super_block);\
}
#define _SQUASHFS_SWAP_DIR_INDEX(s, d, SWAP_FUNC) {\
SWAP_FUNC(32, s, d, index, struct squashfs_dir_index);\
SWAP_FUNC(32, s, d, start_block, struct squashfs_dir_index);\
SWAP_FUNC(32, s, d, size, struct squashfs_dir_index);\
}
#define _SQUASHFS_SWAP_BASE_INODE_HEADER(s, d, SWAP_FUNC) {\
SWAP_FUNC(16, s, d, inode_type, struct squashfs_base_inode_header);\
SWAP_FUNC(16, s, d, mode, struct squashfs_base_inode_header);\
SWAP_FUNC(16, s, d, uid, struct squashfs_base_inode_header);\
SWAP_FUNC(16, s, d, guid, struct squashfs_base_inode_header);\
SWAP_FUNC(32, s, d, mtime, struct squashfs_base_inode_header);\
SWAP_FUNC(32, s, d, inode_number, struct squashfs_base_inode_header);\
}
#define _SQUASHFS_SWAP_IPC_INODE_HEADER(s, d, SWAP_FUNC) {\
SWAP_FUNC(16, s, d, inode_type, struct squashfs_ipc_inode_header);\
SWAP_FUNC(16, s, d, mode, struct squashfs_ipc_inode_header);\
SWAP_FUNC(16, s, d, uid, struct squashfs_ipc_inode_header);\
SWAP_FUNC(16, s, d, guid, struct squashfs_ipc_inode_header);\
SWAP_FUNC(32, s, d, mtime, struct squashfs_ipc_inode_header);\
SWAP_FUNC(32, s, d, inode_number, struct squashfs_ipc_inode_header);\
SWAP_FUNC(32, s, d, nlink, struct squashfs_ipc_inode_header);\
}
#define _SQUASHFS_SWAP_LIPC_INODE_HEADER(s, d, SWAP_FUNC) {\
SWAP_FUNC(16, s, d, inode_type, struct squashfs_lipc_inode_header);\
SWAP_FUNC(16, s, d, mode, struct squashfs_lipc_inode_header);\
SWAP_FUNC(16, s, d, uid, struct squashfs_lipc_inode_header);\
SWAP_FUNC(16, s, d, guid, struct squashfs_lipc_inode_header);\
SWAP_FUNC(32, s, d, mtime, struct squashfs_lipc_inode_header);\
SWAP_FUNC(32, s, d, inode_number, struct squashfs_lipc_inode_header);\
SWAP_FUNC(32, s, d, nlink, struct squashfs_lipc_inode_header);\
SWAP_FUNC(32, s, d, xattr, struct squashfs_lipc_inode_header);\
}
#define _SQUASHFS_SWAP_DEV_INODE_HEADER(s, d, SWAP_FUNC) {\
SWAP_FUNC(16, s, d, inode_type, struct squashfs_dev_inode_header);\
SWAP_FUNC(16, s, d, mode, struct squashfs_dev_inode_header);\
SWAP_FUNC(16, s, d, uid, struct squashfs_dev_inode_header);\
SWAP_FUNC(16, s, d, guid, struct squashfs_dev_inode_header);\
SWAP_FUNC(32, s, d, mtime, struct squashfs_dev_inode_header);\
SWAP_FUNC(32, s, d, inode_number, struct squashfs_dev_inode_header);\
SWAP_FUNC(32, s, d, nlink, struct squashfs_dev_inode_header);\
SWAP_FUNC(32, s, d, rdev, struct squashfs_dev_inode_header);\
}
#define _SQUASHFS_SWAP_LDEV_INODE_HEADER(s, d, SWAP_FUNC) {\
SWAP_FUNC(16, s, d, inode_type, struct squashfs_ldev_inode_header);\
SWAP_FUNC(16, s, d, mode, struct squashfs_ldev_inode_header);\
SWAP_FUNC(16, s, d, uid, struct squashfs_ldev_inode_header);\
SWAP_FUNC(16, s, d, guid, struct squashfs_ldev_inode_header);\
SWAP_FUNC(32, s, d, mtime, struct squashfs_ldev_inode_header);\
SWAP_FUNC(32, s, d, inode_number, struct squashfs_ldev_inode_header);\
SWAP_FUNC(32, s, d, nlink, struct squashfs_ldev_inode_header);\
SWAP_FUNC(32, s, d, rdev, struct squashfs_ldev_inode_header);\
SWAP_FUNC(32, s, d, xattr, struct squashfs_ldev_inode_header);\
}
#define _SQUASHFS_SWAP_SYMLINK_INODE_HEADER(s, d, SWAP_FUNC) {\
SWAP_FUNC(16, s, d, inode_type, struct squashfs_symlink_inode_header);\
SWAP_FUNC(16, s, d, mode, struct squashfs_symlink_inode_header);\
SWAP_FUNC(16, s, d, uid, struct squashfs_symlink_inode_header);\
SWAP_FUNC(16, s, d, guid, struct squashfs_symlink_inode_header);\
SWAP_FUNC(32, s, d, mtime, struct squashfs_symlink_inode_header);\
SWAP_FUNC(32, s, d, inode_number, struct squashfs_symlink_inode_header);\
SWAP_FUNC(32, s, d, nlink, struct squashfs_symlink_inode_header);\
SWAP_FUNC(32, s, d, symlink_size, struct squashfs_symlink_inode_header);\
}
#define _SQUASHFS_SWAP_REG_INODE_HEADER(s, d, SWAP_FUNC) {\
SWAP_FUNC(16, s, d, inode_type, struct squashfs_reg_inode_header);\
SWAP_FUNC(16, s, d, mode, struct squashfs_reg_inode_header);\
SWAP_FUNC(16, s, d, uid, struct squashfs_reg_inode_header);\
SWAP_FUNC(16, s, d, guid, struct squashfs_reg_inode_header);\
SWAP_FUNC(32, s, d, mtime, struct squashfs_reg_inode_header);\
SWAP_FUNC(32, s, d, inode_number, struct squashfs_reg_inode_header);\
SWAP_FUNC(32, s, d, start_block, struct squashfs_reg_inode_header);\
SWAP_FUNC(32, s, d, fragment, struct squashfs_reg_inode_header);\
SWAP_FUNC(32, s, d, offset, struct squashfs_reg_inode_header);\
SWAP_FUNC(32, s, d, file_size, struct squashfs_reg_inode_header);\
}
#define _SQUASHFS_SWAP_LREG_INODE_HEADER(s, d, SWAP_FUNC) {\
SWAP_FUNC(16, s, d, inode_type, struct squashfs_lreg_inode_header);\
SWAP_FUNC(16, s, d, mode, struct squashfs_lreg_inode_header);\
SWAP_FUNC(16, s, d, uid, struct squashfs_lreg_inode_header);\
SWAP_FUNC(16, s, d, guid, struct squashfs_lreg_inode_header);\
SWAP_FUNC(32, s, d, mtime, struct squashfs_lreg_inode_header);\
SWAP_FUNC(32, s, d, inode_number, struct squashfs_lreg_inode_header);\
SWAP_FUNC(64, s, d, start_block, struct squashfs_lreg_inode_header);\
SWAP_FUNC(64, s, d, file_size, struct squashfs_lreg_inode_header);\
SWAP_FUNC(64, s, d, sparse, struct squashfs_lreg_inode_header);\
SWAP_FUNC(32, s, d, nlink, struct squashfs_lreg_inode_header);\
SWAP_FUNC(32, s, d, fragment, struct squashfs_lreg_inode_header);\
SWAP_FUNC(32, s, d, offset, struct squashfs_lreg_inode_header);\
SWAP_FUNC(32, s, d, xattr, struct squashfs_lreg_inode_header);\
}
#define _SQUASHFS_SWAP_DIR_INODE_HEADER(s, d, SWAP_FUNC) {\
SWAP_FUNC(16, s, d, inode_type, struct squashfs_dir_inode_header);\
SWAP_FUNC(16, s, d, mode, struct squashfs_dir_inode_header);\
SWAP_FUNC(16, s, d, uid, struct squashfs_dir_inode_header);\
SWAP_FUNC(16, s, d, guid, struct squashfs_dir_inode_header);\
SWAP_FUNC(32, s, d, mtime, struct squashfs_dir_inode_header);\
SWAP_FUNC(32, s, d, inode_number, struct squashfs_dir_inode_header);\
SWAP_FUNC(32, s, d, start_block, struct squashfs_dir_inode_header);\
SWAP_FUNC(32, s, d, nlink, struct squashfs_dir_inode_header);\
SWAP_FUNC(16, s, d, file_size, struct squashfs_dir_inode_header);\
SWAP_FUNC(16, s, d, offset, struct squashfs_dir_inode_header);\
SWAP_FUNC(32, s, d, parent_inode, struct squashfs_dir_inode_header);\
}
#define _SQUASHFS_SWAP_LDIR_INODE_HEADER(s, d, SWAP_FUNC) {\
SWAP_FUNC(16, s, d, inode_type, struct squashfs_ldir_inode_header);\
SWAP_FUNC(16, s, d, mode, struct squashfs_ldir_inode_header);\
SWAP_FUNC(16, s, d, uid, struct squashfs_ldir_inode_header);\
SWAP_FUNC(16, s, d, guid, struct squashfs_ldir_inode_header);\
SWAP_FUNC(32, s, d, mtime, struct squashfs_ldir_inode_header);\
SWAP_FUNC(32, s, d, inode_number, struct squashfs_ldir_inode_header);\
SWAP_FUNC(32, s, d, nlink, struct squashfs_ldir_inode_header);\
SWAP_FUNC(32, s, d, file_size, struct squashfs_ldir_inode_header);\
SWAP_FUNC(32, s, d, start_block, struct squashfs_ldir_inode_header);\
SWAP_FUNC(32, s, d, parent_inode, struct squashfs_ldir_inode_header);\
SWAP_FUNC(16, s, d, i_count, struct squashfs_ldir_inode_header);\
SWAP_FUNC(16, s, d, offset, struct squashfs_ldir_inode_header);\
SWAP_FUNC(32, s, d, xattr, struct squashfs_ldir_inode_header);\
}
#define _SQUASHFS_SWAP_DIR_ENTRY(s, d, SWAP_FUNC) {\
SWAP_FUNC(16, s, d, offset, struct squashfs_dir_entry);\
SWAP_FUNC##S(16, s, d, inode_number, struct squashfs_dir_entry);\
SWAP_FUNC(16, s, d, type, struct squashfs_dir_entry);\
SWAP_FUNC(16, s, d, size, struct squashfs_dir_entry);\
}
#define _SQUASHFS_SWAP_DIR_HEADER(s, d, SWAP_FUNC) {\
SWAP_FUNC(32, s, d, count, struct squashfs_dir_header);\
SWAP_FUNC(32, s, d, start_block, struct squashfs_dir_header);\
SWAP_FUNC(32, s, d, inode_number, struct squashfs_dir_header);\
}
#define _SQUASHFS_SWAP_FRAGMENT_ENTRY(s, d, SWAP_FUNC) {\
SWAP_FUNC(64, s, d, start_block, struct squashfs_fragment_entry);\
SWAP_FUNC(32, s, d, size, struct squashfs_fragment_entry);\
}
#define _SQUASHFS_SWAP_XATTR_ENTRY(s, d, SWAP_FUNC) {\
SWAP_FUNC(16, s, d, type, struct squashfs_xattr_entry);\
SWAP_FUNC(16, s, d, size, struct squashfs_xattr_entry);\
}
#define _SQUASHFS_SWAP_XATTR_VAL(s, d, SWAP_FUNC) {\
SWAP_FUNC(32, s, d, vsize, struct squashfs_xattr_val);\
}
#define _SQUASHFS_SWAP_XATTR_ID(s, d, SWAP_FUNC) {\
SWAP_FUNC(64, s, d, xattr, struct squashfs_xattr_id);\
SWAP_FUNC(32, s, d, count, struct squashfs_xattr_id);\
SWAP_FUNC(32, s, d, size, struct squashfs_xattr_id);\
}
#define _SQUASHFS_SWAP_XATTR_TABLE(s, d, SWAP_FUNC) {\
SWAP_FUNC(64, s, d, xattr_table_start, struct squashfs_xattr_table);\
SWAP_FUNC(32, s, d, xattr_ids, struct squashfs_xattr_table);\
}
/* big endian architecture copy and swap macros */
#define SQUASHFS_SWAP_SUPER_BLOCK(s, d) \
_SQUASHFS_SWAP_SUPER_BLOCK(s, d, SWAP_LE)
#define SQUASHFS_SWAP_DIR_INDEX(s, d) \
_SQUASHFS_SWAP_DIR_INDEX(s, d, SWAP_LE)
#define SQUASHFS_SWAP_BASE_INODE_HEADER(s, d) \
_SQUASHFS_SWAP_BASE_INODE_HEADER(s, d, SWAP_LE)
#define SQUASHFS_SWAP_IPC_INODE_HEADER(s, d) \
_SQUASHFS_SWAP_IPC_INODE_HEADER(s, d, SWAP_LE)
#define SQUASHFS_SWAP_LIPC_INODE_HEADER(s, d) \
_SQUASHFS_SWAP_LIPC_INODE_HEADER(s, d, SWAP_LE)
#define SQUASHFS_SWAP_DEV_INODE_HEADER(s, d) \
_SQUASHFS_SWAP_DEV_INODE_HEADER(s, d, SWAP_LE)
#define SQUASHFS_SWAP_LDEV_INODE_HEADER(s, d) \
_SQUASHFS_SWAP_LDEV_INODE_HEADER(s, d, SWAP_LE)
#define SQUASHFS_SWAP_SYMLINK_INODE_HEADER(s, d) \
_SQUASHFS_SWAP_SYMLINK_INODE_HEADER(s, d, SWAP_LE)
#define SQUASHFS_SWAP_REG_INODE_HEADER(s, d) \
_SQUASHFS_SWAP_REG_INODE_HEADER(s, d, SWAP_LE)
#define SQUASHFS_SWAP_LREG_INODE_HEADER(s, d) \
_SQUASHFS_SWAP_LREG_INODE_HEADER(s, d, SWAP_LE)
#define SQUASHFS_SWAP_DIR_INODE_HEADER(s, d) \
_SQUASHFS_SWAP_DIR_INODE_HEADER(s, d, SWAP_LE)
#define SQUASHFS_SWAP_LDIR_INODE_HEADER(s, d) \
_SQUASHFS_SWAP_LDIR_INODE_HEADER(s, d, SWAP_LE)
#define SQUASHFS_SWAP_DIR_ENTRY(s, d) \
_SQUASHFS_SWAP_DIR_ENTRY(s, d, SWAP_LE)
#define SQUASHFS_SWAP_DIR_HEADER(s, d) \
_SQUASHFS_SWAP_DIR_HEADER(s, d, SWAP_LE)
#define SQUASHFS_SWAP_FRAGMENT_ENTRY(s, d) \
_SQUASHFS_SWAP_FRAGMENT_ENTRY(s, d, SWAP_LE)
#define SQUASHFS_SWAP_XATTR_ENTRY(s, d) \
_SQUASHFS_SWAP_XATTR_ENTRY(s, d, SWAP_LE)
#define SQUASHFS_SWAP_XATTR_VAL(s, d) \
_SQUASHFS_SWAP_XATTR_VAL(s, d, SWAP_LE)
#define SQUASHFS_SWAP_XATTR_ID(s, d) \
_SQUASHFS_SWAP_XATTR_ID(s, d, SWAP_LE)
#define SQUASHFS_SWAP_XATTR_TABLE(s, d) \
_SQUASHFS_SWAP_XATTR_TABLE(s, d, SWAP_LE)
#define SWAP_LE(bits, s, d, field, type) \
SWAP_LE##bits(((void *)(s)) + offsetof(type, field), \
((void *)(d)) + offsetof(type, field))
#define SWAP_LES(bits, s, d, field, type) \
SWAP_LE(bits, s, d, field, type)
#define SQUASHFS_SWAP_INODE_T(s, d) SQUASHFS_SWAP_LONG_LONGS(s, d, 1)
#define SQUASHFS_SWAP_FRAGMENT_INDEXES(s, d, n) \
SQUASHFS_SWAP_LONG_LONGS(s, d, n)
#define SQUASHFS_SWAP_LOOKUP_BLOCKS(s, d, n) SQUASHFS_SWAP_LONG_LONGS(s, d, n)
#define SQUASHFS_SWAP_ID_BLOCKS(s, d, n) SQUASHFS_SWAP_LONG_LONGS(s, d, n)
#define SQUASHFS_SWAP_SHORTS(s, d, n) swap_le16_num(s, d, n)
#define SQUASHFS_SWAP_INTS(s, d, n) swap_le32_num(s, d, n)
#define SQUASHFS_SWAP_LONG_LONGS(s, d, n) swap_le64_num(s, d, n)
#define SWAP_LE16(s, d) swap_le16(s, d)
#define SWAP_LE32(s, d) swap_le32(s, d)
#define SWAP_LE64(s, d) swap_le64(s, d)
/* big endian architecture swap in-place macros */
#define SQUASHFS_INSWAP_SUPER_BLOCK(s) \
_SQUASHFS_SWAP_SUPER_BLOCK(s, s, INSWAP_LE)
#define SQUASHFS_INSWAP_DIR_INDEX(s) \
_SQUASHFS_SWAP_DIR_INDEX(s, s, INSWAP_LE)
#define SQUASHFS_INSWAP_BASE_INODE_HEADER(s) \
_SQUASHFS_SWAP_BASE_INODE_HEADER(s, s, INSWAP_LE)
#define SQUASHFS_INSWAP_IPC_INODE_HEADER(s) \
_SQUASHFS_SWAP_IPC_INODE_HEADER(s, s, INSWAP_LE)
#define SQUASHFS_INSWAP_LIPC_INODE_HEADER(s) \
_SQUASHFS_SWAP_LIPC_INODE_HEADER(s, s, INSWAP_LE)
#define SQUASHFS_INSWAP_DEV_INODE_HEADER(s) \
_SQUASHFS_SWAP_DEV_INODE_HEADER(s, s, INSWAP_LE)
#define SQUASHFS_INSWAP_LDEV_INODE_HEADER(s) \
_SQUASHFS_SWAP_LDEV_INODE_HEADER(s, s, INSWAP_LE)
#define SQUASHFS_INSWAP_SYMLINK_INODE_HEADER(s) \
_SQUASHFS_SWAP_SYMLINK_INODE_HEADER(s, s, INSWAP_LE)
#define SQUASHFS_INSWAP_REG_INODE_HEADER(s) \
_SQUASHFS_SWAP_REG_INODE_HEADER(s, s, INSWAP_LE)
#define SQUASHFS_INSWAP_LREG_INODE_HEADER(s) \
_SQUASHFS_SWAP_LREG_INODE_HEADER(s, s, INSWAP_LE)
#define SQUASHFS_INSWAP_DIR_INODE_HEADER(s) \
_SQUASHFS_SWAP_DIR_INODE_HEADER(s, s, INSWAP_LE)
#define SQUASHFS_INSWAP_LDIR_INODE_HEADER(s) \
_SQUASHFS_SWAP_LDIR_INODE_HEADER(s, s, INSWAP_LE)
#define SQUASHFS_INSWAP_DIR_ENTRY(s) \
_SQUASHFS_SWAP_DIR_ENTRY(s, s, INSWAP_LE)
#define SQUASHFS_INSWAP_DIR_HEADER(s) \
_SQUASHFS_SWAP_DIR_HEADER(s, s, INSWAP_LE)
#define SQUASHFS_INSWAP_FRAGMENT_ENTRY(s) \
_SQUASHFS_SWAP_FRAGMENT_ENTRY(s, s, INSWAP_LE)
#define SQUASHFS_INSWAP_XATTR_ENTRY(s) \
_SQUASHFS_SWAP_XATTR_ENTRY(s, s, INSWAP_LE)
#define SQUASHFS_INSWAP_XATTR_VAL(s) \
_SQUASHFS_SWAP_XATTR_VAL(s, s, INSWAP_LE)
#define SQUASHFS_INSWAP_XATTR_ID(s) \
_SQUASHFS_SWAP_XATTR_ID(s, s, INSWAP_LE)
#define SQUASHFS_INSWAP_XATTR_TABLE(s) \
_SQUASHFS_SWAP_XATTR_TABLE(s, s, INSWAP_LE)
#define INSWAP_LE(bits, s, d, field, type) \
(s)->field = inswap_le##bits((s)->field)
#define INSWAP_LES(bits, s, d, field, type) \
(s)->field = (short) inswap_le##bits((unsigned short) \
(s)->field)
#define SQUASHFS_INSWAP_INODE_T(s) s = inswap_le64(s)
#define SQUASHFS_INSWAP_FRAGMENT_INDEXES(s, n) inswap_le64_num(s, n)
#define SQUASHFS_INSWAP_LOOKUP_BLOCKS(s, n) inswap_le64_num(s, n)
#define SQUASHFS_INSWAP_ID_BLOCKS(s, n) inswap_le64_num(s, n)
#define SQUASHFS_INSWAP_SHORTS(s, n) inswap_le16_num(s, n)
#define SQUASHFS_INSWAP_INTS(s, n) inswap_le32_num(s, n)
#define SQUASHFS_INSWAP_LONG_LONGS(s, n) inswap_le64_num(s, n)
#else
/* little endian architecture, just copy */
#define SQUASHFS_SWAP_SUPER_BLOCK(s, d) \
SQUASHFS_MEMCPY(s, d, sizeof(struct squashfs_super_block))
#define SQUASHFS_SWAP_DIR_INDEX(s, d) \
SQUASHFS_MEMCPY(s, d, sizeof(struct squashfs_dir_index))
#define SQUASHFS_SWAP_BASE_INODE_HEADER(s, d) \
SQUASHFS_MEMCPY(s, d, sizeof(struct squashfs_base_inode_header))
#define SQUASHFS_SWAP_IPC_INODE_HEADER(s, d) \
SQUASHFS_MEMCPY(s, d, sizeof(struct squashfs_ipc_inode_header))
#define SQUASHFS_SWAP_LIPC_INODE_HEADER(s, d) \
SQUASHFS_MEMCPY(s, d, sizeof(struct squashfs_lipc_inode_header))
#define SQUASHFS_SWAP_DEV_INODE_HEADER(s, d) \
SQUASHFS_MEMCPY(s, d, sizeof(struct squashfs_dev_inode_header))
#define SQUASHFS_SWAP_LDEV_INODE_HEADER(s, d) \
SQUASHFS_MEMCPY(s, d, sizeof(struct squashfs_ldev_inode_header))
#define SQUASHFS_SWAP_SYMLINK_INODE_HEADER(s, d) \
SQUASHFS_MEMCPY(s, d, sizeof(struct squashfs_symlink_inode_header))
#define SQUASHFS_SWAP_REG_INODE_HEADER(s, d) \
SQUASHFS_MEMCPY(s, d, sizeof(struct squashfs_reg_inode_header))
#define SQUASHFS_SWAP_LREG_INODE_HEADER(s, d) \
SQUASHFS_MEMCPY(s, d, sizeof(struct squashfs_lreg_inode_header))
#define SQUASHFS_SWAP_DIR_INODE_HEADER(s, d) \
SQUASHFS_MEMCPY(s, d, sizeof(struct squashfs_dir_inode_header))
#define SQUASHFS_SWAP_LDIR_INODE_HEADER(s, d) \
SQUASHFS_MEMCPY(s, d, sizeof(struct squashfs_ldir_inode_header))
#define SQUASHFS_SWAP_DIR_ENTRY(s, d) \
SQUASHFS_MEMCPY(s, d, sizeof(struct squashfs_dir_entry))
#define SQUASHFS_SWAP_DIR_HEADER(s, d) \
SQUASHFS_MEMCPY(s, d, sizeof(struct squashfs_dir_header))
#define SQUASHFS_SWAP_FRAGMENT_ENTRY(s, d) \
SQUASHFS_MEMCPY(s, d, sizeof(struct squashfs_fragment_entry))
#define SQUASHFS_SWAP_XATTR_ENTRY(s, d) \
SQUASHFS_MEMCPY(s, d, sizeof(struct squashfs_xattr_entry))
#define SQUASHFS_SWAP_XATTR_VAL(s, d) \
SQUASHFS_MEMCPY(s, d, sizeof(struct squashfs_xattr_val))
#define SQUASHFS_SWAP_XATTR_ID(s, d) \
SQUASHFS_MEMCPY(s, d, sizeof(struct squashfs_xattr_id))
#define SQUASHFS_SWAP_XATTR_TABLE(s, d) \
SQUASHFS_MEMCPY(s, d, sizeof(struct squashfs_xattr_table))
#define SQUASHFS_SWAP_INODE_T(s, d) SQUASHFS_SWAP_LONG_LONGS(s, d, 1)
#define SQUASHFS_SWAP_FRAGMENT_INDEXES(s, d, n) \
SQUASHFS_SWAP_LONG_LONGS(s, d, n)
#define SQUASHFS_SWAP_LOOKUP_BLOCKS(s, d, n) SQUASHFS_SWAP_LONG_LONGS(s, d, n)
#define SQUASHFS_SWAP_ID_BLOCKS(s, d, n) SQUASHFS_SWAP_LONG_LONGS(s, d, n)
#define SQUASHFS_MEMCPY(s, d, n) memcpy(d, s, n)
#define SQUASHFS_SWAP_SHORTS(s, d, n) memcpy(d, s, n * sizeof(short))
#define SQUASHFS_SWAP_INTS(s, d, n) memcpy(d, s, n * sizeof(int))
#define SQUASHFS_SWAP_LONG_LONGS(s, d, n) \
memcpy(d, s, n * sizeof(long long))
/* little endian architecture, data already in place so do nothing */
#define SQUASHFS_INSWAP_SUPER_BLOCK(s)
#define SQUASHFS_INSWAP_DIR_INDEX(s)
#define SQUASHFS_INSWAP_BASE_INODE_HEADER(s)
#define SQUASHFS_INSWAP_IPC_INODE_HEADER(s)
#define SQUASHFS_INSWAP_LIPC_INODE_HEADER(s)
#define SQUASHFS_INSWAP_DEV_INODE_HEADER(s)
#define SQUASHFS_INSWAP_LDEV_INODE_HEADER(s)
#define SQUASHFS_INSWAP_SYMLINK_INODE_HEADER(s)
#define SQUASHFS_INSWAP_REG_INODE_HEADER(s)
#define SQUASHFS_INSWAP_LREG_INODE_HEADER(s)
#define SQUASHFS_INSWAP_DIR_INODE_HEADER(s)
#define SQUASHFS_INSWAP_LDIR_INODE_HEADER(s)
#define SQUASHFS_INSWAP_DIR_ENTRY(s)
#define SQUASHFS_INSWAP_DIR_HEADER(s)
#define SQUASHFS_INSWAP_FRAGMENT_ENTRY(s)
#define SQUASHFS_INSWAP_XATTR_ENTRY(s)
#define SQUASHFS_INSWAP_XATTR_VAL(s)
#define SQUASHFS_INSWAP_XATTR_ID(s)
#define SQUASHFS_INSWAP_XATTR_TABLE(s)
#define SQUASHFS_INSWAP_INODE_T(s)
#define SQUASHFS_INSWAP_FRAGMENT_INDEXES(s, n)
#define SQUASHFS_INSWAP_LOOKUP_BLOCKS(s, n)
#define SQUASHFS_INSWAP_ID_BLOCKS(s, n)
#define SQUASHFS_INSWAP_SHORTS(s, n)
#define SQUASHFS_INSWAP_INTS(s, n)
#define SQUASHFS_INSWAP_LONG_LONGS(s, n)
#endif
#endif

View file

@ -0,0 +1,123 @@
/*
* Copyright (c) 2009, 2010
* 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.
*
* swap.c
*/
#ifndef linux
#define __BYTE_ORDER BYTE_ORDER
#define __BIG_ENDIAN BIG_ENDIAN
#define __LITTLE_ENDIAN LITTLE_ENDIAN
#else
#include <endian.h>
#endif
#if __BYTE_ORDER == __BIG_ENDIAN
void swap_le16(void *src, void *dest)
{
unsigned char *s = src;
unsigned char *d = dest;
d[0] = s[1];
d[1] = s[0];
}
void swap_le32(void *src, void *dest)
{
unsigned char *s = src;
unsigned char *d = dest;
d[0] = s[3];
d[1] = s[2];
d[2] = s[1];
d[3] = s[0];
}
void swap_le64(void *src, void *dest)
{
unsigned char *s = src;
unsigned char *d = dest;
d[0] = s[7];
d[1] = s[6];
d[2] = s[5];
d[3] = s[4];
d[4] = s[3];
d[5] = s[2];
d[6] = s[1];
d[7] = s[0];
}
unsigned short inswap_le16(unsigned short num)
{
return (num >> 8) |
((num & 0xff) << 8);
}
unsigned int inswap_le32(unsigned int num)
{
return (num >> 24) |
((num & 0xff0000) >> 8) |
((num & 0xff00) << 8) |
((num & 0xff) << 24);
}
long long inswap_le64(long long n)
{
unsigned long long num = n;
return (num >> 56) |
((num & 0xff000000000000LL) >> 40) |
((num & 0xff0000000000LL) >> 24) |
((num & 0xff00000000LL) >> 8) |
((num & 0xff000000) << 8) |
((num & 0xff0000) << 24) |
((num & 0xff00) << 40) |
((num & 0xff) << 56);
}
#define SWAP_LE_NUM(BITS) \
void swap_le##BITS##_num(void *s, void *d, int n) \
{\
int i;\
for(i = 0; i < n; i++, s += BITS / 8, d += BITS / 8)\
swap_le##BITS(s, d);\
}
SWAP_LE_NUM(16)
SWAP_LE_NUM(32)
SWAP_LE_NUM(64)
#define INSWAP_LE_NUM(BITS, TYPE) \
void inswap_le##BITS##_num(TYPE *s, int n) \
{\
int i;\
for(i = 0; i < n; i++)\
s[i] = inswap_le##BITS(s[i]);\
}
INSWAP_LE_NUM(16, unsigned short)
INSWAP_LE_NUM(32, unsigned int)
INSWAP_LE_NUM(64, long long)
#endif

View file

@ -0,0 +1,411 @@
/*
* Unsquash a squashfs filesystem. This is a highly compressed read only
* filesystem.
*
* Copyright (c) 2009, 2010, 2011, 2012, 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.
*
* unsquash-1.c
*/
#include "unsquashfs.h"
#include "squashfs_compat.h"
static unsigned int *uid_table, *guid_table;
static char *inode_table, *directory_table;
static squashfs_operations ops;
static void read_block_list(unsigned int *block_list, char *block_ptr, int blocks)
{
unsigned short block_size;
int i;
TRACE("read_block_list: blocks %d\n", blocks);
for(i = 0; i < blocks; i++, block_ptr += 2) {
if(swap) {
unsigned short sblock_size;
memcpy(&sblock_size, block_ptr, sizeof(unsigned short));
SQUASHFS_SWAP_SHORTS_3((&block_size), &sblock_size, 1);
} else
memcpy(&block_size, block_ptr, sizeof(unsigned short));
block_list[i] = SQUASHFS_COMPRESSED_SIZE(block_size) |
(SQUASHFS_COMPRESSED(block_size) ? 0 :
SQUASHFS_COMPRESSED_BIT_BLOCK);
}
}
static struct inode *read_inode(unsigned int start_block, unsigned int offset)
{
static union squashfs_inode_header_1 header;
long long start = sBlk.s.inode_table_start + start_block;
int bytes = lookup_entry(inode_table_hash, start);
char *block_ptr = inode_table + bytes + offset;
static struct inode i;
TRACE("read_inode: reading inode [%d:%d]\n", start_block, offset);
if(bytes == -1)
EXIT_UNSQUASH("read_inode: inode table block %lld not found\n",
start);
if(swap) {
squashfs_base_inode_header_1 sinode;
memcpy(&sinode, block_ptr, sizeof(header.base));
SQUASHFS_SWAP_BASE_INODE_HEADER_1(&header.base, &sinode,
sizeof(squashfs_base_inode_header_1));
} else
memcpy(&header.base, block_ptr, sizeof(header.base));
i.uid = (uid_t) uid_table[(header.base.inode_type - 1) /
SQUASHFS_TYPES * 16 + header.base.uid];
if(header.base.inode_type == SQUASHFS_IPC_TYPE) {
squashfs_ipc_inode_header_1 *inodep = &header.ipc;
if(swap) {
squashfs_ipc_inode_header_1 sinodep;
memcpy(&sinodep, block_ptr, sizeof(sinodep));
SQUASHFS_SWAP_IPC_INODE_HEADER_1(inodep, &sinodep);
} else
memcpy(inodep, block_ptr, sizeof(*inodep));
if(inodep->type == SQUASHFS_SOCKET_TYPE) {
i.mode = S_IFSOCK | header.base.mode;
i.type = SQUASHFS_SOCKET_TYPE;
} else {
i.mode = S_IFIFO | header.base.mode;
i.type = SQUASHFS_FIFO_TYPE;
}
i.uid = (uid_t) uid_table[inodep->offset * 16 + inodep->uid];
} else {
i.mode = lookup_type[(header.base.inode_type - 1) %
SQUASHFS_TYPES + 1] | header.base.mode;
i.type = (header.base.inode_type - 1) % SQUASHFS_TYPES + 1;
}
i.xattr = SQUASHFS_INVALID_XATTR;
i.gid = header.base.guid == 15 ? i.uid :
(uid_t) guid_table[header.base.guid];
i.time = sBlk.s.mkfs_time;
i.inode_number = inode_number ++;
switch(i.type) {
case SQUASHFS_DIR_TYPE: {
squashfs_dir_inode_header_1 *inode = &header.dir;
if(swap) {
squashfs_dir_inode_header_1 sinode;
memcpy(&sinode, block_ptr, sizeof(header.dir));
SQUASHFS_SWAP_DIR_INODE_HEADER_1(inode,
&sinode);
} else
memcpy(inode, block_ptr, sizeof(header.dir));
i.data = inode->file_size;
i.offset = inode->offset;
i.start = inode->start_block;
i.time = inode->mtime;
break;
}
case SQUASHFS_FILE_TYPE: {
squashfs_reg_inode_header_1 *inode = &header.reg;
if(swap) {
squashfs_reg_inode_header_1 sinode;
memcpy(&sinode, block_ptr, sizeof(sinode));
SQUASHFS_SWAP_REG_INODE_HEADER_1(inode,
&sinode);
} else
memcpy(inode, block_ptr, sizeof(*inode));
i.data = inode->file_size;
i.time = inode->mtime;
i.blocks = (i.data + sBlk.s.block_size - 1) >>
sBlk.s.block_log;
i.start = inode->start_block;
i.block_ptr = block_ptr + sizeof(*inode);
i.fragment = 0;
i.frag_bytes = 0;
i.offset = 0;
i.sparse = 0;
break;
}
case SQUASHFS_SYMLINK_TYPE: {
squashfs_symlink_inode_header_1 *inodep =
&header.symlink;
if(swap) {
squashfs_symlink_inode_header_1 sinodep;
memcpy(&sinodep, block_ptr, sizeof(sinodep));
SQUASHFS_SWAP_SYMLINK_INODE_HEADER_1(inodep,
&sinodep);
} else
memcpy(inodep, block_ptr, sizeof(*inodep));
i.symlink = malloc(inodep->symlink_size + 1);
if(i.symlink == NULL)
EXIT_UNSQUASH("read_inode: failed to malloc "
"symlink data\n");
strncpy(i.symlink, block_ptr +
sizeof(squashfs_symlink_inode_header_1),
inodep->symlink_size);
i.symlink[inodep->symlink_size] = '\0';
i.data = inodep->symlink_size;
break;
}
case SQUASHFS_BLKDEV_TYPE:
case SQUASHFS_CHRDEV_TYPE: {
squashfs_dev_inode_header_1 *inodep = &header.dev;
if(swap) {
squashfs_dev_inode_header_1 sinodep;
memcpy(&sinodep, block_ptr, sizeof(sinodep));
SQUASHFS_SWAP_DEV_INODE_HEADER_1(inodep,
&sinodep);
} else
memcpy(inodep, block_ptr, sizeof(*inodep));
i.data = inodep->rdev;
break;
}
case SQUASHFS_FIFO_TYPE:
case SQUASHFS_SOCKET_TYPE: {
i.data = 0;
break;
}
default:
EXIT_UNSQUASH("Unknown inode type %d in "
" read_inode_header_1!\n",
header.base.inode_type);
}
return &i;
}
static struct dir *squashfs_opendir(unsigned int block_start, unsigned int offset,
struct inode **i)
{
squashfs_dir_header_2 dirh;
char buffer[sizeof(squashfs_dir_entry_2) + SQUASHFS_NAME_LEN + 1]
__attribute__((aligned));
squashfs_dir_entry_2 *dire = (squashfs_dir_entry_2 *) buffer;
long long start;
int bytes;
int dir_count, size;
struct dir_ent *new_dir;
struct dir *dir;
TRACE("squashfs_opendir: inode start block %d, offset %d\n",
block_start, offset);
*i = read_inode(block_start, offset);
dir = malloc(sizeof(struct dir));
if(dir == NULL)
EXIT_UNSQUASH("squashfs_opendir: malloc failed!\n");
dir->dir_count = 0;
dir->cur_entry = 0;
dir->mode = (*i)->mode;
dir->uid = (*i)->uid;
dir->guid = (*i)->gid;
dir->mtime = (*i)->time;
dir->xattr = (*i)->xattr;
dir->dirs = NULL;
if ((*i)->data == 0)
/*
* if the directory is empty, skip the unnecessary
* lookup_entry, this fixes the corner case with
* completely empty filesystems where lookup_entry correctly
* returning -1 is incorrectly treated as an error
*/
return dir;
start = sBlk.s.directory_table_start + (*i)->start;
bytes = lookup_entry(directory_table_hash, start);
if(bytes == -1)
EXIT_UNSQUASH("squashfs_opendir: directory block %d not "
"found!\n", block_start);
bytes += (*i)->offset;
size = (*i)->data + bytes;
while(bytes < size) {
if(swap) {
squashfs_dir_header_2 sdirh;
memcpy(&sdirh, directory_table + bytes, sizeof(sdirh));
SQUASHFS_SWAP_DIR_HEADER_2(&dirh, &sdirh);
} else
memcpy(&dirh, directory_table + bytes, sizeof(dirh));
dir_count = dirh.count + 1;
TRACE("squashfs_opendir: Read directory header @ byte position "
"%d, %d directory entries\n", bytes, dir_count);
bytes += sizeof(dirh);
/* dir_count should never be larger than SQUASHFS_DIR_COUNT */
if(dir_count > SQUASHFS_DIR_COUNT) {
ERROR("File system corrupted: too many entries in directory\n");
goto corrupted;
}
while(dir_count--) {
if(swap) {
squashfs_dir_entry_2 sdire;
memcpy(&sdire, directory_table + bytes,
sizeof(sdire));
SQUASHFS_SWAP_DIR_ENTRY_2(dire, &sdire);
} else
memcpy(dire, directory_table + bytes,
sizeof(*dire));
bytes += sizeof(*dire);
/* size should never be SQUASHFS_NAME_LEN or larger */
if(dire->size >= SQUASHFS_NAME_LEN) {
ERROR("File system corrupted: filename too long\n");
goto corrupted;
}
memcpy(dire->name, directory_table + bytes,
dire->size + 1);
dire->name[dire->size + 1] = '\0';
TRACE("squashfs_opendir: directory entry %s, inode "
"%d:%d, type %d\n", dire->name,
dirh.start_block, dire->offset, dire->type);
if((dir->dir_count % DIR_ENT_SIZE) == 0) {
new_dir = realloc(dir->dirs, (dir->dir_count +
DIR_ENT_SIZE) * sizeof(struct dir_ent));
if(new_dir == NULL)
EXIT_UNSQUASH("squashfs_opendir: "
"realloc failed!\n");
dir->dirs = new_dir;
}
strcpy(dir->dirs[dir->dir_count].name, dire->name);
dir->dirs[dir->dir_count].start_block =
dirh.start_block;
dir->dirs[dir->dir_count].offset = dire->offset;
dir->dirs[dir->dir_count].type = dire->type;
dir->dir_count ++;
bytes += dire->size + 1;
}
}
return dir;
corrupted:
free(dir->dirs);
free(dir);
return NULL;
}
squashfs_operations *read_filesystem_tables_1()
{
long long table_start;
/* Read uid and gid lookup tables */
/* Sanity check super block contents */
if(sBlk.no_guids) {
if(sBlk.guid_start >= sBlk.s.bytes_used) {
ERROR("read_filesystem_tables: gid start too large in super block\n");
goto corrupted;
}
/* In 1.x filesystems, there should never be more than 15 gids */
if(sBlk.no_guids > 15) {
ERROR("read_filesystem_tables: gids too large in super block\n");
goto corrupted;
}
if(read_ids(sBlk.no_guids, sBlk.guid_start, sBlk.s.bytes_used, &guid_table) == FALSE)
goto corrupted;
table_start = sBlk.guid_start;
} else {
/* no guids, guid_start should be 0 */
if(sBlk.guid_start != 0) {
ERROR("read_filesystem_tables: gid start too large in super block\n");
goto corrupted;
}
table_start = sBlk.s.bytes_used;
}
if(sBlk.uid_start >= table_start) {
ERROR("read_filesystem_tables: uid start too large in super block\n");
goto corrupted;
}
/* There should be at least one uid */
if(sBlk.no_uids == 0) {
ERROR("read_filesystem_tables: uid count bad in super block\n");
goto corrupted;
}
/* In 1.x filesystems, there should never be more than 48 uids */
if(sBlk.no_uids > 48) {
ERROR("read_filesystem_tables: uids too large in super block\n");
goto corrupted;
}
if(read_ids(sBlk.no_uids, sBlk.uid_start, table_start, &uid_table) == FALSE)
goto corrupted;
table_start = sBlk.uid_start;
/* Read directory table */
/* Sanity check super block contents */
if(sBlk.s.directory_table_start > table_start) {
ERROR("read_filesystem_tables: directory table start too large in super block\n");
goto corrupted;
}
directory_table = read_directory_table(sBlk.s.directory_table_start,
table_start);
if(directory_table == NULL)
goto corrupted;
/* Read inode table */
/* Sanity check super block contents */
if(sBlk.s.inode_table_start >= sBlk.s.directory_table_start) {
ERROR("read_filesystem_tables: inode table start too large in super block\n");
goto corrupted;
}
inode_table = read_inode_table(sBlk.s.inode_table_start,
sBlk.s.directory_table_start);
if(inode_table == NULL)
goto corrupted;
return &ops;
corrupted:
ERROR("File system corruption detected\n");
return NULL;
}
static squashfs_operations ops = {
.opendir = squashfs_opendir,
.read_block_list = read_block_list,
.read_inode = read_inode
};

View file

@ -0,0 +1,83 @@
/*
* Unsquash a squashfs filesystem. This is a highly compressed read only
* filesystem.
*
* Copyright (c) 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.
*
* unsquash-123.c
*
* Helper functions used by unsquash-1, unsquash-2 and unsquash-3.
*/
#include "unsquashfs.h"
#include "squashfs_compat.h"
int read_ids(int ids, long long start, long long end, unsigned int **id_table)
{
/* Note on overflow limits:
* Size of ids is 2^8
* Max length is 2^8*4 or 1024
*/
int res;
int length = ids * sizeof(unsigned int);
/*
* The size of the index table (length bytes) should match the
* table start and end points
*/
if(length != (end - start)) {
ERROR("read_ids: Bad inode count in super block\n");
return FALSE;
}
TRACE("read_ids: no_ids %d\n", ids);
*id_table = malloc(length);
if(*id_table == NULL) {
ERROR("read_ids: failed to allocate uid/gid table\n");
return FALSE;
}
if(swap) {
unsigned int *sid_table = malloc(length);
if(sid_table == NULL) {
ERROR("read_ids: failed to allocate uid/gid table\n");
return FALSE;
}
res = read_fs_bytes(fd, start, length, sid_table);
if(res == FALSE) {
ERROR("read_ids: failed to read uid/gid table"
"\n");
free(sid_table);
return FALSE;
}
SQUASHFS_SWAP_INTS_3((*id_table), sid_table, ids);
free(sid_table);
} else {
res = read_fs_bytes(fd, start, length, *id_table);
if(res == FALSE) {
ERROR("read_ids: failed to read uid/gid table"
"\n");
return FALSE;
}
}
return TRUE;
}

View file

@ -0,0 +1,529 @@
/*
* Unsquash a squashfs filesystem. This is a highly compressed read only
* filesystem.
*
* Copyright (c) 2009, 2010, 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.
*
* unsquash-2.c
*/
#include "unsquashfs.h"
#include "squashfs_compat.h"
static squashfs_fragment_entry_2 *fragment_table;
static unsigned int *uid_table, *guid_table;
static char *inode_table, *directory_table;
static squashfs_operations ops;
static void read_block_list(unsigned int *block_list, char *block_ptr, int blocks)
{
TRACE("read_block_list: blocks %d\n", blocks);
if(swap) {
SQUASHFS_SWAP_INTS_3(block_list, block_ptr, blocks);
} else
memcpy(block_list, block_ptr, blocks * sizeof(unsigned int));
}
static int read_fragment_table(long long *table_start)
{
/*
* Note on overflow limits:
* Size of SBlk.s.fragments is 2^32 (unsigned int)
* Max size of bytes is 2^32*8 or 2^35
* Max indexes is (2^32*8)/8K or 2^22
* Max length is ((2^32*8)/8K)*4 or 2^24 or 16M
*/
int res, i;
long long bytes = SQUASHFS_FRAGMENT_BYTES_2((long long) sBlk.s.fragments);
int indexes = SQUASHFS_FRAGMENT_INDEXES_2((long long) sBlk.s.fragments);
int length = SQUASHFS_FRAGMENT_INDEX_BYTES_2((long long) sBlk.s.fragments);
unsigned int *fragment_table_index;
/*
* The size of the index table (length bytes) should match the
* table start and end points
*/
if(length != (*table_start- sBlk.s.fragment_table_start)) {
ERROR("read_ids: Bad inode count in super block\n");
return FALSE;
}
TRACE("read_fragment_table: %d fragments, reading %d fragment indexes "
"from 0x%llx\n", sBlk.s.fragments, indexes,
sBlk.s.fragment_table_start);
fragment_table_index = malloc(length);
if(fragment_table_index == NULL)
EXIT_UNSQUASH("read_fragment_table: failed to allocate "
"fragment table index\n");
fragment_table = malloc(bytes);
if(fragment_table == NULL)
EXIT_UNSQUASH("read_fragment_table: failed to allocate "
"fragment table\n");
if(swap) {
unsigned int *sfragment_table_index = malloc(length);
if(sfragment_table_index == NULL)
EXIT_UNSQUASH("read_fragment_table: failed to allocate "
"fragment table index\n");
res = read_fs_bytes(fd, sBlk.s.fragment_table_start,
length, sfragment_table_index);
if(res == FALSE) {
ERROR("read_fragment_table: failed to read fragment "
"table index\n");
free(sfragment_table_index);
goto failed;
}
SQUASHFS_SWAP_FRAGMENT_INDEXES_2(fragment_table_index,
sfragment_table_index, indexes);
free(sfragment_table_index);
} else {
res = read_fs_bytes(fd, sBlk.s.fragment_table_start,
length, fragment_table_index);
if(res == FALSE) {
ERROR("read_fragment_table: failed to read fragment "
"table index\n");
goto failed;
}
}
for(i = 0; i < indexes; i++) {
int expected = (i + 1) != indexes ? SQUASHFS_METADATA_SIZE :
bytes & (SQUASHFS_METADATA_SIZE - 1);
int length = read_block(fd, fragment_table_index[i], NULL,
expected, ((char *) fragment_table) + ((long long) i *
SQUASHFS_METADATA_SIZE));
TRACE("Read fragment table block %d, from 0x%x, length %d\n", i,
fragment_table_index[i], length);
if(length == FALSE) {
ERROR("read_fragment_table: failed to read fragment "
"table block\n");
goto failed;
}
}
if(swap) {
squashfs_fragment_entry_2 sfragment;
for(i = 0; i < sBlk.s.fragments; i++) {
SQUASHFS_SWAP_FRAGMENT_ENTRY_2((&sfragment),
(&fragment_table[i]));
memcpy((char *) &fragment_table[i], (char *) &sfragment,
sizeof(squashfs_fragment_entry_2));
}
}
*table_start = fragment_table_index[0];
free(fragment_table_index);
return TRUE;
failed:
free(fragment_table_index);
return FALSE;
}
static void read_fragment(unsigned int fragment, long long *start_block, int *size)
{
TRACE("read_fragment: reading fragment %d\n", fragment);
squashfs_fragment_entry_2 *fragment_entry = &fragment_table[fragment];
*start_block = fragment_entry->start_block;
*size = fragment_entry->size;
}
static struct inode *read_inode(unsigned int start_block, unsigned int offset)
{
static union squashfs_inode_header_2 header;
long long start = sBlk.s.inode_table_start + start_block;
int bytes = lookup_entry(inode_table_hash, start);
char *block_ptr = inode_table + bytes + offset;
static struct inode i;
TRACE("read_inode: reading inode [%d:%d]\n", start_block, offset);
if(bytes == -1)
EXIT_UNSQUASH("read_inode: inode table block %lld not found\n",
start);
if(swap) {
squashfs_base_inode_header_2 sinode;
memcpy(&sinode, block_ptr, sizeof(header.base));
SQUASHFS_SWAP_BASE_INODE_HEADER_2(&header.base, &sinode,
sizeof(squashfs_base_inode_header_2));
} else
memcpy(&header.base, block_ptr, sizeof(header.base));
i.xattr = SQUASHFS_INVALID_XATTR;
i.uid = (uid_t) uid_table[header.base.uid];
i.gid = header.base.guid == SQUASHFS_GUIDS ? i.uid :
(uid_t) guid_table[header.base.guid];
i.mode = lookup_type[header.base.inode_type] | header.base.mode;
i.type = header.base.inode_type;
i.time = sBlk.s.mkfs_time;
i.inode_number = inode_number++;
switch(header.base.inode_type) {
case SQUASHFS_DIR_TYPE: {
squashfs_dir_inode_header_2 *inode = &header.dir;
if(swap) {
squashfs_dir_inode_header_2 sinode;
memcpy(&sinode, block_ptr, sizeof(header.dir));
SQUASHFS_SWAP_DIR_INODE_HEADER_2(&header.dir,
&sinode);
} else
memcpy(&header.dir, block_ptr,
sizeof(header.dir));
i.data = inode->file_size;
i.offset = inode->offset;
i.start = inode->start_block;
i.time = inode->mtime;
break;
}
case SQUASHFS_LDIR_TYPE: {
squashfs_ldir_inode_header_2 *inode = &header.ldir;
if(swap) {
squashfs_ldir_inode_header_2 sinode;
memcpy(&sinode, block_ptr, sizeof(header.ldir));
SQUASHFS_SWAP_LDIR_INODE_HEADER_2(&header.ldir,
&sinode);
} else
memcpy(&header.ldir, block_ptr,
sizeof(header.ldir));
i.data = inode->file_size;
i.offset = inode->offset;
i.start = inode->start_block;
i.time = inode->mtime;
break;
}
case SQUASHFS_FILE_TYPE: {
squashfs_reg_inode_header_2 *inode = &header.reg;
if(swap) {
squashfs_reg_inode_header_2 sinode;
memcpy(&sinode, block_ptr, sizeof(sinode));
SQUASHFS_SWAP_REG_INODE_HEADER_2(inode,
&sinode);
} else
memcpy(inode, block_ptr, sizeof(*inode));
i.data = inode->file_size;
i.time = inode->mtime;
i.frag_bytes = inode->fragment == SQUASHFS_INVALID_FRAG
? 0 : inode->file_size % sBlk.s.block_size;
i.fragment = inode->fragment;
i.offset = inode->offset;
i.blocks = inode->fragment == SQUASHFS_INVALID_FRAG ?
(i.data + sBlk.s.block_size - 1) >>
sBlk.s.block_log : i.data >>
sBlk.s.block_log;
i.start = inode->start_block;
i.sparse = 0;
i.block_ptr = block_ptr + sizeof(*inode);
break;
}
case SQUASHFS_SYMLINK_TYPE: {
squashfs_symlink_inode_header_2 *inodep =
&header.symlink;
if(swap) {
squashfs_symlink_inode_header_2 sinodep;
memcpy(&sinodep, block_ptr, sizeof(sinodep));
SQUASHFS_SWAP_SYMLINK_INODE_HEADER_2(inodep,
&sinodep);
} else
memcpy(inodep, block_ptr, sizeof(*inodep));
i.symlink = malloc(inodep->symlink_size + 1);
if(i.symlink == NULL)
EXIT_UNSQUASH("read_inode: failed to malloc "
"symlink data\n");
strncpy(i.symlink, block_ptr +
sizeof(squashfs_symlink_inode_header_2),
inodep->symlink_size);
i.symlink[inodep->symlink_size] = '\0';
i.data = inodep->symlink_size;
break;
}
case SQUASHFS_BLKDEV_TYPE:
case SQUASHFS_CHRDEV_TYPE: {
squashfs_dev_inode_header_2 *inodep = &header.dev;
if(swap) {
squashfs_dev_inode_header_2 sinodep;
memcpy(&sinodep, block_ptr, sizeof(sinodep));
SQUASHFS_SWAP_DEV_INODE_HEADER_2(inodep,
&sinodep);
} else
memcpy(inodep, block_ptr, sizeof(*inodep));
i.data = inodep->rdev;
break;
}
case SQUASHFS_FIFO_TYPE:
case SQUASHFS_SOCKET_TYPE:
i.data = 0;
break;
default:
EXIT_UNSQUASH("Unknown inode type %d in "
"read_inode_header_2!\n",
header.base.inode_type);
}
return &i;
}
static struct dir *squashfs_opendir(unsigned int block_start, unsigned int offset,
struct inode **i)
{
squashfs_dir_header_2 dirh;
char buffer[sizeof(squashfs_dir_entry_2) + SQUASHFS_NAME_LEN + 1]
__attribute__((aligned));
squashfs_dir_entry_2 *dire = (squashfs_dir_entry_2 *) buffer;
long long start;
int bytes;
int dir_count, size;
struct dir_ent *new_dir;
struct dir *dir;
TRACE("squashfs_opendir: inode start block %d, offset %d\n",
block_start, offset);
*i = read_inode(block_start, offset);
dir = malloc(sizeof(struct dir));
if(dir == NULL)
EXIT_UNSQUASH("squashfs_opendir: malloc failed!\n");
dir->dir_count = 0;
dir->cur_entry = 0;
dir->mode = (*i)->mode;
dir->uid = (*i)->uid;
dir->guid = (*i)->gid;
dir->mtime = (*i)->time;
dir->xattr = (*i)->xattr;
dir->dirs = NULL;
if ((*i)->data == 0)
/*
* if the directory is empty, skip the unnecessary
* lookup_entry, this fixes the corner case with
* completely empty filesystems where lookup_entry correctly
* returning -1 is incorrectly treated as an error
*/
return dir;
start = sBlk.s.directory_table_start + (*i)->start;
bytes = lookup_entry(directory_table_hash, start);
if(bytes == -1)
EXIT_UNSQUASH("squashfs_opendir: directory block %d not "
"found!\n", block_start);
bytes += (*i)->offset;
size = (*i)->data + bytes;
while(bytes < size) {
if(swap) {
squashfs_dir_header_2 sdirh;
memcpy(&sdirh, directory_table + bytes, sizeof(sdirh));
SQUASHFS_SWAP_DIR_HEADER_2(&dirh, &sdirh);
} else
memcpy(&dirh, directory_table + bytes, sizeof(dirh));
dir_count = dirh.count + 1;
TRACE("squashfs_opendir: Read directory header @ byte position "
"%d, %d directory entries\n", bytes, dir_count);
bytes += sizeof(dirh);
/* dir_count should never be larger than SQUASHFS_DIR_COUNT */
if(dir_count > SQUASHFS_DIR_COUNT) {
ERROR("File system corrupted: too many entries in directory\n");
goto corrupted;
}
while(dir_count--) {
if(swap) {
squashfs_dir_entry_2 sdire;
memcpy(&sdire, directory_table + bytes,
sizeof(sdire));
SQUASHFS_SWAP_DIR_ENTRY_2(dire, &sdire);
} else
memcpy(dire, directory_table + bytes,
sizeof(*dire));
bytes += sizeof(*dire);
/* size should never be SQUASHFS_NAME_LEN or larger */
if(dire->size >= SQUASHFS_NAME_LEN) {
ERROR("File system corrupted: filename too long\n");
goto corrupted;
}
memcpy(dire->name, directory_table + bytes,
dire->size + 1);
dire->name[dire->size + 1] = '\0';
TRACE("squashfs_opendir: directory entry %s, inode "
"%d:%d, type %d\n", dire->name,
dirh.start_block, dire->offset, dire->type);
if((dir->dir_count % DIR_ENT_SIZE) == 0) {
new_dir = realloc(dir->dirs, (dir->dir_count +
DIR_ENT_SIZE) * sizeof(struct dir_ent));
if(new_dir == NULL)
EXIT_UNSQUASH("squashfs_opendir: "
"realloc failed!\n");
dir->dirs = new_dir;
}
strcpy(dir->dirs[dir->dir_count].name, dire->name);
dir->dirs[dir->dir_count].start_block =
dirh.start_block;
dir->dirs[dir->dir_count].offset = dire->offset;
dir->dirs[dir->dir_count].type = dire->type;
dir->dir_count ++;
bytes += dire->size + 1;
}
}
return dir;
corrupted:
free(dir->dirs);
free(dir);
return NULL;
}
squashfs_operations *read_filesystem_tables_2()
{
long long table_start;
/* Read uid and gid lookup tables */
/* Sanity check super block contents */
if(sBlk.no_guids) {
if(sBlk.guid_start >= sBlk.s.bytes_used) {
ERROR("read_filesystem_tables: gid start too large in super block\n");
goto corrupted;
}
if(read_ids(sBlk.no_guids, sBlk.guid_start, sBlk.s.bytes_used, &guid_table) == FALSE)
goto corrupted;
table_start = sBlk.guid_start;
} else {
/* no guids, guid_start should be 0 */
if(sBlk.guid_start != 0) {
ERROR("read_filesystem_tables: gid start too large in super block\n");
goto corrupted;
}
table_start = sBlk.s.bytes_used;
}
if(sBlk.uid_start >= table_start) {
ERROR("read_filesystem_tables: uid start too large in super block\n");
goto corrupted;
}
/* There should be at least one uid */
if(sBlk.no_uids == 0) {
ERROR("read_filesystem_tables: uid count bad in super block\n");
goto corrupted;
}
if(read_ids(sBlk.no_uids, sBlk.uid_start, table_start, &uid_table) == FALSE)
goto corrupted;
table_start = sBlk.uid_start;
/* Read fragment table */
if(sBlk.s.fragments != 0) {
/* Sanity check super block contents */
if(sBlk.s.fragment_table_start >= table_start) {
ERROR("read_filesystem_tables: fragment table start too large in super block\n");
goto corrupted;
}
/* The number of fragments should not exceed the number of inodes */
if(sBlk.s.fragments > sBlk.s.inodes) {
ERROR("read_filesystem_tables: Bad fragment count in super block\n");
goto corrupted;
}
if(read_fragment_table(&table_start) == FALSE)
goto corrupted;
} else {
/*
* Sanity check super block contents - with 0 fragments,
* the fragment table should be empty
*/
if(sBlk.s.fragment_table_start != table_start) {
ERROR("read_filesystem_tables: fragment table start invalid in super block\n");
goto corrupted;
}
}
/* Read directory table */
/* Sanity check super block contents */
if(sBlk.s.directory_table_start > table_start) {
ERROR("read_filesystem_tables: directory table start too large in super block\n");
goto corrupted;
}
directory_table = read_directory_table(sBlk.s.directory_table_start,
table_start);
if(directory_table == NULL)
goto corrupted;
/* Read inode table */
/* Sanity check super block contents */
if(sBlk.s.inode_table_start >= sBlk.s.directory_table_start) {
ERROR("read_filesystem_tables: inode table start too large in super block\n");
goto corrupted;
}
inode_table = read_inode_table(sBlk.s.inode_table_start,
sBlk.s.directory_table_start);
if(inode_table == NULL)
goto corrupted;
return &ops;
corrupted:
ERROR("File system corruption detected\n");
return NULL;
}
static squashfs_operations ops = {
.opendir = squashfs_opendir,
.read_fragment = read_fragment,
.read_block_list = read_block_list,
.read_inode = read_inode
};

View file

@ -0,0 +1,632 @@
/*
* Unsquash a squashfs filesystem. This is a highly compressed read only
* filesystem.
*
* Copyright (c) 2009, 2010, 2011, 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.
*
* unsquash-3.c
*/
#include "unsquashfs.h"
#include "squashfs_compat.h"
static squashfs_fragment_entry_3 *fragment_table;
static unsigned int *uid_table, *guid_table;
static char *inode_table, *directory_table;
static squashfs_operations ops;
static long long *salloc_index_table(int indexes)
{
static long long *alloc_table = NULL;
static int alloc_size = 0;
int length = indexes * sizeof(long long);
if(alloc_size < length || length == 0) {
long long *table = realloc(alloc_table, length);
if(table == NULL && length !=0 )
EXIT_UNSQUASH("alloc_index_table: failed to allocate "
"index table\n");
alloc_table = table;
alloc_size = length;
}
return alloc_table;
}
static void read_block_list(unsigned int *block_list, char *block_ptr, int blocks)
{
TRACE("read_block_list: blocks %d\n", blocks);
if(swap) {
SQUASHFS_SWAP_INTS_3(block_list, block_ptr, blocks);
} else
memcpy(block_list, block_ptr, blocks * sizeof(unsigned int));
}
static int read_fragment_table(long long *table_start)
{
/*
* Note on overflow limits:
* Size of SBlk.s.fragments 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 length is ((2^32*16)/8K)*8 or 2^26 or 64M
*/
int res, i;
long long bytes = SQUASHFS_FRAGMENT_BYTES_3((long long) sBlk.s.fragments);
int indexes = SQUASHFS_FRAGMENT_INDEXES_3((long long) sBlk.s.fragments);
int length = SQUASHFS_FRAGMENT_INDEX_BYTES_3((long long) sBlk.s.fragments);
long long *fragment_table_index;
/*
* The size of the index table (length bytes) should match the
* table start and end points
*/
if(length != (*table_start - sBlk.s.fragment_table_start)) {
ERROR("read_fragment_table: Bad fragment count in super block\n");
return FALSE;
}
TRACE("read_fragment_table: %d fragments, reading %d fragment indexes "
"from 0x%llx\n", sBlk.s.fragments, indexes,
sBlk.s.fragment_table_start);
fragment_table_index = alloc_index_table(indexes);
fragment_table = malloc(bytes);
if(fragment_table == NULL)
EXIT_UNSQUASH("read_fragment_table: failed to allocate "
"fragment table\n");
if(swap) {
long long *sfragment_table_index = salloc_index_table(indexes);
res = read_fs_bytes(fd, sBlk.s.fragment_table_start,
length, sfragment_table_index);
if(res == FALSE) {
ERROR("read_fragment_table: failed to read fragment "
"table index\n");
return FALSE;
}
SQUASHFS_SWAP_FRAGMENT_INDEXES_3(fragment_table_index,
sfragment_table_index, indexes);
} else {
res = read_fs_bytes(fd, sBlk.s.fragment_table_start,
length, fragment_table_index);
if(res == FALSE) {
ERROR("read_fragment_table: failed to read fragment "
"table index\n");
return FALSE;
}
}
for(i = 0; i < indexes; i++) {
int expected = (i + 1) != indexes ? SQUASHFS_METADATA_SIZE :
bytes & (SQUASHFS_METADATA_SIZE - 1);
int length = read_block(fd, fragment_table_index[i], NULL,
expected, ((char *) fragment_table) + ((long long) i *
SQUASHFS_METADATA_SIZE));
TRACE("Read fragment table block %d, from 0x%llx, length %d\n",
i, fragment_table_index[i], length);
if(length == FALSE) {
ERROR("read_fragment_table: failed to read fragment "
"table block\n");
return FALSE;
}
}
if(swap) {
squashfs_fragment_entry_3 sfragment;
for(i = 0; i < sBlk.s.fragments; i++) {
SQUASHFS_SWAP_FRAGMENT_ENTRY_3((&sfragment),
(&fragment_table[i]));
memcpy((char *) &fragment_table[i], (char *) &sfragment,
sizeof(squashfs_fragment_entry_3));
}
}
*table_start = fragment_table_index[0];
return TRUE;
}
static void read_fragment(unsigned int fragment, long long *start_block, int *size)
{
TRACE("read_fragment: reading fragment %d\n", fragment);
squashfs_fragment_entry_3 *fragment_entry = &fragment_table[fragment];
*start_block = fragment_entry->start_block;
*size = fragment_entry->size;
}
static struct inode *read_inode(unsigned int start_block, unsigned int offset)
{
static union squashfs_inode_header_3 header;
long long start = sBlk.s.inode_table_start + start_block;
int bytes = lookup_entry(inode_table_hash, start);
char *block_ptr = inode_table + bytes + offset;
static struct inode i;
TRACE("read_inode: reading inode [%d:%d]\n", start_block, offset);
if(bytes == -1)
EXIT_UNSQUASH("read_inode: inode table block %lld not found\n",
start);
if(swap) {
squashfs_base_inode_header_3 sinode;
memcpy(&sinode, block_ptr, sizeof(header.base));
SQUASHFS_SWAP_BASE_INODE_HEADER_3(&header.base, &sinode,
sizeof(squashfs_base_inode_header_3));
} else
memcpy(&header.base, block_ptr, sizeof(header.base));
i.xattr = SQUASHFS_INVALID_XATTR;
i.uid = (uid_t) uid_table[header.base.uid];
i.gid = header.base.guid == SQUASHFS_GUIDS ? i.uid :
(uid_t) guid_table[header.base.guid];
i.mode = lookup_type[header.base.inode_type] | header.base.mode;
i.type = header.base.inode_type;
i.time = header.base.mtime;
i.inode_number = header.base.inode_number;
switch(header.base.inode_type) {
case SQUASHFS_DIR_TYPE: {
squashfs_dir_inode_header_3 *inode = &header.dir;
if(swap) {
squashfs_dir_inode_header_3 sinode;
memcpy(&sinode, block_ptr, sizeof(header.dir));
SQUASHFS_SWAP_DIR_INODE_HEADER_3(&header.dir,
&sinode);
} else
memcpy(&header.dir, block_ptr,
sizeof(header.dir));
i.data = inode->file_size;
i.offset = inode->offset;
i.start = inode->start_block;
break;
}
case SQUASHFS_LDIR_TYPE: {
squashfs_ldir_inode_header_3 *inode = &header.ldir;
if(swap) {
squashfs_ldir_inode_header_3 sinode;
memcpy(&sinode, block_ptr, sizeof(header.ldir));
SQUASHFS_SWAP_LDIR_INODE_HEADER_3(&header.ldir,
&sinode);
} else
memcpy(&header.ldir, block_ptr,
sizeof(header.ldir));
i.data = inode->file_size;
i.offset = inode->offset;
i.start = inode->start_block;
break;
}
case SQUASHFS_FILE_TYPE: {
squashfs_reg_inode_header_3 *inode = &header.reg;
if(swap) {
squashfs_reg_inode_header_3 sinode;
memcpy(&sinode, block_ptr, sizeof(sinode));
SQUASHFS_SWAP_REG_INODE_HEADER_3(inode,
&sinode);
} else
memcpy(inode, block_ptr, sizeof(*inode));
i.data = inode->file_size;
i.frag_bytes = inode->fragment == SQUASHFS_INVALID_FRAG
? 0 : inode->file_size % sBlk.s.block_size;
i.fragment = inode->fragment;
i.offset = inode->offset;
i.blocks = inode->fragment == SQUASHFS_INVALID_FRAG ?
(i.data + sBlk.s.block_size - 1) >>
sBlk.s.block_log :
i.data >> sBlk.s.block_log;
i.start = inode->start_block;
i.sparse = 1;
i.block_ptr = block_ptr + sizeof(*inode);
break;
}
case SQUASHFS_LREG_TYPE: {
squashfs_lreg_inode_header_3 *inode = &header.lreg;
if(swap) {
squashfs_lreg_inode_header_3 sinode;
memcpy(&sinode, block_ptr, sizeof(sinode));
SQUASHFS_SWAP_LREG_INODE_HEADER_3(inode,
&sinode);
} else
memcpy(inode, block_ptr, sizeof(*inode));
i.data = inode->file_size;
i.frag_bytes = inode->fragment == SQUASHFS_INVALID_FRAG
? 0 : inode->file_size % sBlk.s.block_size;
i.fragment = inode->fragment;
i.offset = inode->offset;
i.blocks = inode->fragment == SQUASHFS_INVALID_FRAG ?
(inode->file_size + sBlk.s.block_size - 1) >>
sBlk.s.block_log :
inode->file_size >> sBlk.s.block_log;
i.start = inode->start_block;
i.sparse = 1;
i.block_ptr = block_ptr + sizeof(*inode);
break;
}
case SQUASHFS_SYMLINK_TYPE: {
squashfs_symlink_inode_header_3 *inodep =
&header.symlink;
if(swap) {
squashfs_symlink_inode_header_3 sinodep;
memcpy(&sinodep, block_ptr, sizeof(sinodep));
SQUASHFS_SWAP_SYMLINK_INODE_HEADER_3(inodep,
&sinodep);
} else
memcpy(inodep, block_ptr, sizeof(*inodep));
i.symlink = malloc(inodep->symlink_size + 1);
if(i.symlink == NULL)
EXIT_UNSQUASH("read_inode: failed to malloc "
"symlink data\n");
strncpy(i.symlink, block_ptr +
sizeof(squashfs_symlink_inode_header_3),
inodep->symlink_size);
i.symlink[inodep->symlink_size] = '\0';
i.data = inodep->symlink_size;
break;
}
case SQUASHFS_BLKDEV_TYPE:
case SQUASHFS_CHRDEV_TYPE: {
squashfs_dev_inode_header_3 *inodep = &header.dev;
if(swap) {
squashfs_dev_inode_header_3 sinodep;
memcpy(&sinodep, block_ptr, sizeof(sinodep));
SQUASHFS_SWAP_DEV_INODE_HEADER_3(inodep,
&sinodep);
} else
memcpy(inodep, block_ptr, sizeof(*inodep));
i.data = inodep->rdev;
break;
}
case SQUASHFS_FIFO_TYPE:
case SQUASHFS_SOCKET_TYPE:
i.data = 0;
break;
default:
EXIT_UNSQUASH("Unknown inode type %d in read_inode!\n",
header.base.inode_type);
}
return &i;
}
static struct dir *squashfs_opendir(unsigned int block_start, unsigned int offset,
struct inode **i)
{
squashfs_dir_header_3 dirh;
char buffer[sizeof(squashfs_dir_entry_3) + SQUASHFS_NAME_LEN + 1]
__attribute__((aligned));
squashfs_dir_entry_3 *dire = (squashfs_dir_entry_3 *) buffer;
long long start;
int bytes;
int dir_count, size;
struct dir_ent *new_dir;
struct dir *dir;
TRACE("squashfs_opendir: inode start block %d, offset %d\n",
block_start, offset);
*i = read_inode(block_start, offset);
dir = malloc(sizeof(struct dir));
if(dir == NULL)
EXIT_UNSQUASH("squashfs_opendir: malloc failed!\n");
dir->dir_count = 0;
dir->cur_entry = 0;
dir->mode = (*i)->mode;
dir->uid = (*i)->uid;
dir->guid = (*i)->gid;
dir->mtime = (*i)->time;
dir->xattr = (*i)->xattr;
dir->dirs = NULL;
if ((*i)->data == 3)
/*
* if the directory is empty, skip the unnecessary
* lookup_entry, this fixes the corner case with
* completely empty filesystems where lookup_entry correctly
* returning -1 is incorrectly treated as an error
*/
return dir;
start = sBlk.s.directory_table_start + (*i)->start;
bytes = lookup_entry(directory_table_hash, start);
if(bytes == -1)
EXIT_UNSQUASH("squashfs_opendir: directory block %d not "
"found!\n", block_start);
bytes += (*i)->offset;
size = (*i)->data + bytes - 3;
while(bytes < size) {
if(swap) {
squashfs_dir_header_3 sdirh;
memcpy(&sdirh, directory_table + bytes, sizeof(sdirh));
SQUASHFS_SWAP_DIR_HEADER_3(&dirh, &sdirh);
} else
memcpy(&dirh, directory_table + bytes, sizeof(dirh));
dir_count = dirh.count + 1;
TRACE("squashfs_opendir: Read directory header @ byte position "
"%d, %d directory entries\n", bytes, dir_count);
bytes += sizeof(dirh);
/* dir_count should never be larger than SQUASHFS_DIR_COUNT */
if(dir_count > SQUASHFS_DIR_COUNT) {
ERROR("File system corrupted: too many entries in directory\n");
goto corrupted;
}
while(dir_count--) {
if(swap) {
squashfs_dir_entry_3 sdire;
memcpy(&sdire, directory_table + bytes,
sizeof(sdire));
SQUASHFS_SWAP_DIR_ENTRY_3(dire, &sdire);
} else
memcpy(dire, directory_table + bytes,
sizeof(*dire));
bytes += sizeof(*dire);
/* size should never be SQUASHFS_NAME_LEN or larger */
if(dire->size >= SQUASHFS_NAME_LEN) {
ERROR("File system corrupted: filename too long\n");
goto corrupted;
}
memcpy(dire->name, directory_table + bytes,
dire->size + 1);
dire->name[dire->size + 1] = '\0';
TRACE("squashfs_opendir: directory entry %s, inode "
"%d:%d, type %d\n", dire->name,
dirh.start_block, dire->offset, dire->type);
if((dir->dir_count % DIR_ENT_SIZE) == 0) {
new_dir = realloc(dir->dirs, (dir->dir_count +
DIR_ENT_SIZE) * sizeof(struct dir_ent));
if(new_dir == NULL)
EXIT_UNSQUASH("squashfs_opendir: "
"realloc failed!\n");
dir->dirs = new_dir;
}
strcpy(dir->dirs[dir->dir_count].name, dire->name);
dir->dirs[dir->dir_count].start_block =
dirh.start_block;
dir->dirs[dir->dir_count].offset = dire->offset;
dir->dirs[dir->dir_count].type = dire->type;
dir->dir_count ++;
bytes += dire->size + 1;
}
}
return dir;
corrupted:
free(dir->dirs);
free(dir);
return NULL;
}
static int parse_exports_table(long long *table_start)
{
/*
* Note on overflow limits:
* Size of SBlk.s.inodes is 2^32 (unsigned int)
* Max indexes is (2^32*8)/8K or 2^22
* Max length is ((2^32*8)/8K)*8 or 2^25
*/
int res;
int indexes = SQUASHFS_LOOKUP_BLOCKS_3((long long) sBlk.s.inodes);
int length = SQUASHFS_LOOKUP_BLOCK_BYTES_3((long long) sBlk.s.inodes);
long long *export_index_table;
/*
* The size of the index table (length bytes) should match the
* table start and end points
*/
if(length != (*table_start - sBlk.s.lookup_table_start)) {
ERROR("parse_exports_table: Bad inode count in super block\n");
return FALSE;
}
export_index_table = alloc_index_table(indexes);
if(swap) {
long long *sexport_index_table = salloc_index_table(indexes);
res = read_fs_bytes(fd, sBlk.s.lookup_table_start,
length, sexport_index_table);
if(res == FALSE) {
ERROR("parse_exorts_table: failed to read export "
"index table\n");
return FALSE;
}
SQUASHFS_SWAP_LOOKUP_BLOCKS_3(export_index_table,
sexport_index_table, indexes);
} else {
res = read_fs_bytes(fd, sBlk.s.lookup_table_start, length,
export_index_table);
if(res == FALSE) {
ERROR("parse_exorts_table: failed to read export "
"index table\n");
return FALSE;
}
}
/*
* export_index_table[0] stores the start of the compressed export blocks.
* This by definition is also the end of the previous filesystem
* table - the fragment table.
*/
*table_start = export_index_table[0];
return TRUE;
}
squashfs_operations *read_filesystem_tables_3()
{
long long table_start;
/* Read uid and gid lookup tables */
/* Sanity check super block contents */
if(sBlk.no_guids) {
if(sBlk.guid_start >= sBlk.s.bytes_used) {
ERROR("read_filesystem_tables: gid start too large in super block\n");
goto corrupted;
}
if(read_ids(sBlk.no_guids, sBlk.guid_start, sBlk.s.bytes_used, &guid_table) == FALSE)
goto corrupted;
table_start = sBlk.guid_start;
} else {
/* no guids, guid_start should be 0 */
if(sBlk.guid_start != 0) {
ERROR("read_filesystem_tables: gid start too large in super block\n");
goto corrupted;
}
table_start = sBlk.s.bytes_used;
}
if(sBlk.uid_start >= table_start) {
ERROR("read_filesystem_tables: uid start too large in super block\n");
goto corrupted;
}
/* There should be at least one uid */
if(sBlk.no_uids == 0) {
ERROR("read_filesystem_tables: uid count bad in super block\n");
goto corrupted;
}
if(read_ids(sBlk.no_uids, sBlk.uid_start, table_start, &uid_table) == FALSE)
goto corrupted;
table_start = sBlk.uid_start;
/* Read exports table */
if(sBlk.s.lookup_table_start != SQUASHFS_INVALID_BLK) {
/* sanity check super block contents */
if(sBlk.s.lookup_table_start >= table_start) {
ERROR("read_filesystem_tables: lookup table start too large in super block\n");
goto corrupted;
}
if(parse_exports_table(&table_start) == FALSE)
goto corrupted;
}
/* Read fragment table */
if(sBlk.s.fragments != 0) {
/* Sanity check super block contents */
if(sBlk.s.fragment_table_start >= table_start) {
ERROR("read_filesystem_tables: fragment table start too large in super block\n");
goto corrupted;
}
/* The number of fragments should not exceed the number of inodes */
if(sBlk.s.fragments > sBlk.s.inodes) {
ERROR("read_filesystem_tables: Bad fragment count in super block\n");
goto corrupted;
}
if(read_fragment_table(&table_start) == FALSE)
goto corrupted;
} else {
/*
* Sanity check super block contents - with 0 fragments,
* the fragment table should be empty
*/
if(sBlk.s.fragment_table_start != table_start) {
ERROR("read_filesystem_tables: fragment table start invalid in super block\n");
goto corrupted;
}
}
/* Read directory table */
/* Sanity check super block contents */
if(sBlk.s.directory_table_start > table_start) {
ERROR("read_filesystem_tables: directory table start too large in super block\n");
goto corrupted;
}
directory_table = read_directory_table(sBlk.s.directory_table_start,
table_start);
if(directory_table == NULL)
goto corrupted;
/* Read inode table */
/* Sanity check super block contents */
if(sBlk.s.inode_table_start >= sBlk.s.directory_table_start) {
ERROR("read_filesystem_tables: inode table start too large in super block\n");
goto corrupted;
}
inode_table = read_inode_table(sBlk.s.inode_table_start,
sBlk.s.directory_table_start);
if(inode_table == NULL)
goto corrupted;
alloc_index_table(0);
salloc_index_table(0);
return &ops;
corrupted:
ERROR("File system corruption detected\n");
alloc_index_table(0);
salloc_index_table(0);
return NULL;
}
static squashfs_operations ops = {
.opendir = squashfs_opendir,
.read_fragment = read_fragment,
.read_block_list = read_block_list,
.read_inode = read_inode
};

View file

@ -0,0 +1,47 @@
/*
* Unsquash a squashfs filesystem. This is a highly compressed read only
* filesystem.
*
* Copyright (c) 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.
*
* unsquash-34.c
*
* Helper functions used by unsquash-3 and unsquash-4.
*/
#include "unsquashfs.h"
long long *alloc_index_table(int indexes)
{
static long long *alloc_table = NULL;
static int alloc_size = 0;
int length = indexes * sizeof(long long);
if(alloc_size < length || length == 0) {
long long *table = realloc(alloc_table, length);
if(table == NULL && length !=0)
EXIT_UNSQUASH("alloc_index_table: failed to allocate "
"index table\n");
alloc_table = table;
alloc_size = length;
}
return alloc_table;
}

View file

@ -0,0 +1,621 @@
/*
* Unsquash a squashfs filesystem. This is a highly compressed read only
* filesystem.
*
* Copyright (c) 2009, 2010, 2011, 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.
*
* unsquash-4.c
*/
#include "unsquashfs.h"
#include "squashfs_swap.h"
#include "xattr.h"
static struct squashfs_fragment_entry *fragment_table;
static unsigned int *id_table;
static char *inode_table, *directory_table;
static squashfs_operations ops;
static void read_block_list(unsigned int *block_list, char *block_ptr, int blocks)
{
TRACE("read_block_list: blocks %d\n", blocks);
memcpy(block_list, block_ptr, blocks * sizeof(unsigned int));
SQUASHFS_INSWAP_INTS(block_list, blocks);
}
static int read_fragment_table(long long *table_start)
{
/*
* Note on overflow limits:
* Size of SBlk.s.fragments 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 length is ((2^32*16)/8K)*8 or 2^26 or 64M
*/
int res, i;
long long bytes = SQUASHFS_FRAGMENT_BYTES((long long) sBlk.s.fragments);
int indexes = SQUASHFS_FRAGMENT_INDEXES((long long) sBlk.s.fragments);
int length = SQUASHFS_FRAGMENT_INDEX_BYTES((long long) sBlk.s.fragments);
long long *fragment_table_index;
/*
* The size of the index table (length bytes) should match the
* table start and end points
*/
if(length != (*table_start - sBlk.s.fragment_table_start)) {
ERROR("read_fragment_table: Bad fragment count in super block\n");
return FALSE;
}
TRACE("read_fragment_table: %d fragments, reading %d fragment indexes "
"from 0x%llx\n", sBlk.s.fragments, indexes,
sBlk.s.fragment_table_start);
fragment_table_index = alloc_index_table(indexes);
fragment_table = malloc(bytes);
if(fragment_table == NULL)
EXIT_UNSQUASH("read_fragment_table: failed to allocate "
"fragment table\n");
res = read_fs_bytes(fd, sBlk.s.fragment_table_start, length,
fragment_table_index);
if(res == FALSE) {
ERROR("read_fragment_table: failed to read fragment table "
"index\n");
return FALSE;
}
SQUASHFS_INSWAP_FRAGMENT_INDEXES(fragment_table_index, indexes);
for(i = 0; i < indexes; i++) {
int expected = (i + 1) != indexes ? SQUASHFS_METADATA_SIZE :
bytes & (SQUASHFS_METADATA_SIZE - 1);
int length = read_block(fd, fragment_table_index[i], NULL,
expected, ((char *) fragment_table) + (i *
SQUASHFS_METADATA_SIZE));
TRACE("Read fragment table block %d, from 0x%llx, length %d\n",
i, fragment_table_index[i], length);
if(length == FALSE) {
ERROR("read_fragment_table: failed to read fragment "
"table index\n");
return FALSE;
}
}
for(i = 0; i < sBlk.s.fragments; i++)
SQUASHFS_INSWAP_FRAGMENT_ENTRY(&fragment_table[i]);
*table_start = fragment_table_index[0];
return TRUE;
}
static void read_fragment(unsigned int fragment, long long *start_block, int *size)
{
TRACE("read_fragment: reading fragment %d\n", fragment);
struct squashfs_fragment_entry *fragment_entry;
fragment_entry = &fragment_table[fragment];
*start_block = fragment_entry->start_block;
*size = fragment_entry->size;
}
static struct inode *read_inode(unsigned int start_block, unsigned int offset)
{
static union squashfs_inode_header header;
long long start = sBlk.s.inode_table_start + start_block;
long long bytes = lookup_entry(inode_table_hash, start);
char *block_ptr = inode_table + bytes + offset;
static struct inode i;
TRACE("read_inode: reading inode [%d:%d]\n", start_block, offset);
if(bytes == -1)
EXIT_UNSQUASH("read_inode: inode table block %lld not found\n",
start);
SQUASHFS_SWAP_BASE_INODE_HEADER(block_ptr, &header.base);
i.uid = (uid_t) id_table[header.base.uid];
i.gid = (uid_t) id_table[header.base.guid];
i.mode = lookup_type[header.base.inode_type] | header.base.mode;
i.type = header.base.inode_type;
i.time = header.base.mtime;
i.inode_number = header.base.inode_number;
switch(header.base.inode_type) {
case SQUASHFS_DIR_TYPE: {
struct squashfs_dir_inode_header *inode = &header.dir;
SQUASHFS_SWAP_DIR_INODE_HEADER(block_ptr, inode);
i.data = inode->file_size;
i.offset = inode->offset;
i.start = inode->start_block;
i.xattr = SQUASHFS_INVALID_XATTR;
break;
}
case SQUASHFS_LDIR_TYPE: {
struct squashfs_ldir_inode_header *inode = &header.ldir;
SQUASHFS_SWAP_LDIR_INODE_HEADER(block_ptr, inode);
i.data = inode->file_size;
i.offset = inode->offset;
i.start = inode->start_block;
i.xattr = inode->xattr;
break;
}
case SQUASHFS_FILE_TYPE: {
struct squashfs_reg_inode_header *inode = &header.reg;
SQUASHFS_SWAP_REG_INODE_HEADER(block_ptr, inode);
i.data = inode->file_size;
i.frag_bytes = inode->fragment == SQUASHFS_INVALID_FRAG
? 0 : inode->file_size % sBlk.s.block_size;
i.fragment = inode->fragment;
i.offset = inode->offset;
i.blocks = inode->fragment == SQUASHFS_INVALID_FRAG ?
(i.data + sBlk.s.block_size - 1) >>
sBlk.s.block_log :
i.data >> sBlk.s.block_log;
i.start = inode->start_block;
i.sparse = 0;
i.block_ptr = block_ptr + sizeof(*inode);
i.xattr = SQUASHFS_INVALID_XATTR;
break;
}
case SQUASHFS_LREG_TYPE: {
struct squashfs_lreg_inode_header *inode = &header.lreg;
SQUASHFS_SWAP_LREG_INODE_HEADER(block_ptr, inode);
i.data = inode->file_size;
i.frag_bytes = inode->fragment == SQUASHFS_INVALID_FRAG
? 0 : inode->file_size % sBlk.s.block_size;
i.fragment = inode->fragment;
i.offset = inode->offset;
i.blocks = inode->fragment == SQUASHFS_INVALID_FRAG ?
(inode->file_size + sBlk.s.block_size - 1) >>
sBlk.s.block_log :
inode->file_size >> sBlk.s.block_log;
i.start = inode->start_block;
i.sparse = inode->sparse != 0;
i.block_ptr = block_ptr + sizeof(*inode);
i.xattr = inode->xattr;
break;
}
case SQUASHFS_SYMLINK_TYPE:
case SQUASHFS_LSYMLINK_TYPE: {
struct squashfs_symlink_inode_header *inode = &header.symlink;
SQUASHFS_SWAP_SYMLINK_INODE_HEADER(block_ptr, inode);
i.symlink = malloc(inode->symlink_size + 1);
if(i.symlink == NULL)
EXIT_UNSQUASH("read_inode: failed to malloc "
"symlink data\n");
strncpy(i.symlink, block_ptr +
sizeof(struct squashfs_symlink_inode_header),
inode->symlink_size);
i.symlink[inode->symlink_size] = '\0';
i.data = inode->symlink_size;
if(header.base.inode_type == SQUASHFS_LSYMLINK_TYPE)
SQUASHFS_SWAP_INTS(block_ptr +
sizeof(struct squashfs_symlink_inode_header) +
inode->symlink_size, &i.xattr, 1);
else
i.xattr = SQUASHFS_INVALID_XATTR;
break;
}
case SQUASHFS_BLKDEV_TYPE:
case SQUASHFS_CHRDEV_TYPE: {
struct squashfs_dev_inode_header *inode = &header.dev;
SQUASHFS_SWAP_DEV_INODE_HEADER(block_ptr, inode);
i.data = inode->rdev;
i.xattr = SQUASHFS_INVALID_XATTR;
break;
}
case SQUASHFS_LBLKDEV_TYPE:
case SQUASHFS_LCHRDEV_TYPE: {
struct squashfs_ldev_inode_header *inode = &header.ldev;
SQUASHFS_SWAP_LDEV_INODE_HEADER(block_ptr, inode);
i.data = inode->rdev;
i.xattr = inode->xattr;
break;
}
case SQUASHFS_FIFO_TYPE:
case SQUASHFS_SOCKET_TYPE:
i.data = 0;
i.xattr = SQUASHFS_INVALID_XATTR;
break;
case SQUASHFS_LFIFO_TYPE:
case SQUASHFS_LSOCKET_TYPE: {
struct squashfs_lipc_inode_header *inode = &header.lipc;
SQUASHFS_SWAP_LIPC_INODE_HEADER(block_ptr, inode);
i.data = 0;
i.xattr = inode->xattr;
break;
}
default:
EXIT_UNSQUASH("Unknown inode type %d in read_inode!\n",
header.base.inode_type);
}
return &i;
}
static struct dir *squashfs_opendir(unsigned int block_start, unsigned int offset,
struct inode **i)
{
struct squashfs_dir_header dirh;
char buffer[sizeof(struct squashfs_dir_entry) + SQUASHFS_NAME_LEN + 1]
__attribute__((aligned));
struct squashfs_dir_entry *dire = (struct squashfs_dir_entry *) buffer;
long long start;
long long bytes;
int dir_count, size;
struct dir_ent *new_dir;
struct dir *dir;
TRACE("squashfs_opendir: inode start block %d, offset %d\n",
block_start, offset);
*i = read_inode(block_start, offset);
dir = malloc(sizeof(struct dir));
if(dir == NULL)
EXIT_UNSQUASH("squashfs_opendir: malloc failed!\n");
dir->dir_count = 0;
dir->cur_entry = 0;
dir->mode = (*i)->mode;
dir->uid = (*i)->uid;
dir->guid = (*i)->gid;
dir->mtime = (*i)->time;
dir->xattr = (*i)->xattr;
dir->dirs = NULL;
if ((*i)->data == 3)
/*
* if the directory is empty, skip the unnecessary
* lookup_entry, this fixes the corner case with
* completely empty filesystems where lookup_entry correctly
* returning -1 is incorrectly treated as an error
*/
return dir;
start = sBlk.s.directory_table_start + (*i)->start;
bytes = lookup_entry(directory_table_hash, start);
if(bytes == -1)
EXIT_UNSQUASH("squashfs_opendir: directory block %lld not "
"found!\n", start);
bytes += (*i)->offset;
size = (*i)->data + bytes - 3;
while(bytes < size) {
SQUASHFS_SWAP_DIR_HEADER(directory_table + bytes, &dirh);
dir_count = dirh.count + 1;
TRACE("squashfs_opendir: Read directory header @ byte position "
"%d, %d directory entries\n", bytes, dir_count);
bytes += sizeof(dirh);
/* dir_count should never be larger than SQUASHFS_DIR_COUNT */
if(dir_count > SQUASHFS_DIR_COUNT) {
ERROR("File system corrupted: too many entries in directory\n");
goto corrupted;
}
while(dir_count--) {
SQUASHFS_SWAP_DIR_ENTRY(directory_table + bytes, dire);
bytes += sizeof(*dire);
/* size should never be SQUASHFS_NAME_LEN or larger */
if(dire->size >= SQUASHFS_NAME_LEN) {
ERROR("File system corrupted: filename too long\n");
goto corrupted;
}
memcpy(dire->name, directory_table + bytes,
dire->size + 1);
dire->name[dire->size + 1] = '\0';
TRACE("squashfs_opendir: directory entry %s, inode "
"%d:%d, type %d\n", dire->name,
dirh.start_block, dire->offset, dire->type);
if((dir->dir_count % DIR_ENT_SIZE) == 0) {
new_dir = realloc(dir->dirs, (dir->dir_count +
DIR_ENT_SIZE) * sizeof(struct dir_ent));
if(new_dir == NULL)
EXIT_UNSQUASH("squashfs_opendir: "
"realloc failed!\n");
dir->dirs = new_dir;
}
strcpy(dir->dirs[dir->dir_count].name, dire->name);
dir->dirs[dir->dir_count].start_block =
dirh.start_block;
dir->dirs[dir->dir_count].offset = dire->offset;
dir->dirs[dir->dir_count].type = dire->type;
dir->dir_count ++;
bytes += dire->size + 1;
}
}
return dir;
corrupted:
free(dir->dirs);
free(dir);
return NULL;
}
static int read_id_table(long long *table_start)
{
/*
* Note on overflow limits:
* Size of SBlk.s.no_ids is 2^16 (unsigned short)
* Max size of bytes is 2^16*4 or 256K
* Max indexes is (2^16*4)/8K or 32
* Max length is ((2^16*4)/8K)*8 or 256
*/
int res, i;
int bytes = SQUASHFS_ID_BYTES(sBlk.s.no_ids);
int indexes = SQUASHFS_ID_BLOCKS(sBlk.s.no_ids);
int length = SQUASHFS_ID_BLOCK_BYTES(sBlk.s.no_ids);
long long *id_index_table;
/*
* The size of the index table (length bytes) should match the
* table start and end points
*/
if(length != (*table_start - sBlk.s.id_table_start)) {
ERROR("read_id_table: Bad id count in super block\n");
return FALSE;
}
TRACE("read_id_table: no_ids %d\n", sBlk.s.no_ids);
id_index_table = alloc_index_table(indexes);
id_table = malloc(bytes);
if(id_table == NULL) {
ERROR("read_id_table: failed to allocate id table\n");
return FALSE;
}
res = read_fs_bytes(fd, sBlk.s.id_table_start, length, id_index_table);
if(res == FALSE) {
ERROR("read_id_table: failed to read id index table\n");
return FALSE;
}
SQUASHFS_INSWAP_ID_BLOCKS(id_index_table, indexes);
/*
* id_index_table[0] stores the start of the compressed id blocks.
* This by definition is also the end of the previous filesystem
* table - this may be the exports table if it is present, or the
* fragments table if it isn't.
*/
*table_start = id_index_table[0];
for(i = 0; i < indexes; i++) {
int expected = (i + 1) != indexes ? SQUASHFS_METADATA_SIZE :
bytes & (SQUASHFS_METADATA_SIZE - 1);
res = read_block(fd, id_index_table[i], NULL, expected,
((char *) id_table) + i * SQUASHFS_METADATA_SIZE);
if(res == FALSE) {
ERROR("read_id_table: failed to read id table block"
"\n");
return FALSE;
}
}
SQUASHFS_INSWAP_INTS(id_table, sBlk.s.no_ids);
return TRUE;
}
static int parse_exports_table(long long *table_start)
{
/*
* Note on overflow limits:
* Size of SBlk.s.inodes is 2^32 (unsigned int)
* Max indexes is (2^32*8)/8K or 2^22
* Max length is ((2^32*8)/8K)*8 or 2^25
*/
int res;
int indexes = SQUASHFS_LOOKUP_BLOCKS((long long) sBlk.s.inodes);
int length = SQUASHFS_LOOKUP_BLOCK_BYTES((long long) sBlk.s.inodes);
long long *export_index_table;
/*
* The size of the index table (length bytes) should match the
* table start and end points
*/
if(length != (*table_start - sBlk.s.lookup_table_start)) {
ERROR("parse_exports_table: Bad inode count in super block\n");
return FALSE;
}
export_index_table = alloc_index_table(indexes);
res = read_fs_bytes(fd, sBlk.s.lookup_table_start, length,
export_index_table);
if(res == FALSE) {
ERROR("parse_exports_table: failed to read export index table\n");
return FALSE;
}
SQUASHFS_INSWAP_LOOKUP_BLOCKS(export_index_table, indexes);
/*
* export_index_table[0] stores the start of the compressed export blocks.
* This by definition is also the end of the previous filesystem
* table - the fragment table.
*/
*table_start = export_index_table[0];
return TRUE;
}
squashfs_operations *read_filesystem_tables_4()
{
long long table_start;
/* Read xattrs */
if(sBlk.s.xattr_id_table_start != SQUASHFS_INVALID_BLK) {
/* sanity check super block contents */
if(sBlk.s.xattr_id_table_start >= sBlk.s.bytes_used) {
ERROR("read_filesystem_tables: xattr id table start too large in super block\n");
goto corrupted;
}
if(read_xattrs_from_disk(fd, &sBlk.s, no_xattrs, &table_start) == 0)
goto corrupted;
} else
table_start = sBlk.s.bytes_used;
/* Read id lookup table */
/* Sanity check super block contents */
if(sBlk.s.id_table_start >= table_start) {
ERROR("read_filesystem_tables: id table start too large in super block\n");
goto corrupted;
}
/* there should always be at least one id */
if(sBlk.s.no_ids == 0) {
ERROR("read_filesystem_tables: Bad id count in super block\n");
goto corrupted;
}
/*
* the number of ids can never be more than double the number of inodes
* (the maximum is a unique uid and gid for each inode).
*/
if(sBlk.s.no_ids > (sBlk.s.inodes * 2L)) {
ERROR("read_filesystem_tables: Bad id count in super block\n");
goto corrupted;
}
if(read_id_table(&table_start) == FALSE)
goto corrupted;
/* Read exports table */
if(sBlk.s.lookup_table_start != SQUASHFS_INVALID_BLK) {
/* sanity check super block contents */
if(sBlk.s.lookup_table_start >= table_start) {
ERROR("read_filesystem_tables: lookup table start too large in super block\n");
goto corrupted;
}
if(parse_exports_table(&table_start) == FALSE)
goto corrupted;
}
/* Read fragment table */
if(sBlk.s.fragments != 0) {
/* Sanity check super block contents */
if(sBlk.s.fragment_table_start >= table_start) {
ERROR("read_filesystem_tables: fragment table start too large in super block\n");
goto corrupted;
}
/* The number of fragments should not exceed the number of inodes */
if(sBlk.s.fragments > sBlk.s.inodes) {
ERROR("read_filesystem_tables: Bad fragment count in super block\n");
goto corrupted;
}
if(read_fragment_table(&table_start) == FALSE)
goto corrupted;
} else {
/*
* Sanity check super block contents - with 0 fragments,
* the fragment table should be empty
*/
if(sBlk.s.fragment_table_start != table_start) {
ERROR("read_filesystem_tables: fragment table start invalid in super block\n");
goto corrupted;
}
}
/* Read directory table */
/* Sanity check super block contents */
if(sBlk.s.directory_table_start > table_start) {
ERROR("read_filesystem_tables: directory table start too large in super block\n");
goto corrupted;
}
directory_table = read_directory_table(sBlk.s.directory_table_start,
table_start);
if(directory_table == NULL)
goto corrupted;
/* Read inode table */
/* Sanity check super block contents */
if(sBlk.s.inode_table_start >= sBlk.s.directory_table_start) {
ERROR("read_filesystem_tables: inode table start too large in super block\n");
goto corrupted;
}
inode_table = read_inode_table(sBlk.s.inode_table_start,
sBlk.s.directory_table_start);
if(inode_table == NULL)
goto corrupted;
if(no_xattrs)
sBlk.s.xattr_id_table_start = SQUASHFS_INVALID_BLK;
alloc_index_table(0);
return &ops;
corrupted:
ERROR("File system corruption detected\n");
alloc_index_table(0);
return NULL;
}
static squashfs_operations ops = {
.opendir = squashfs_opendir,
.read_fragment = read_fragment,
.read_block_list = read_block_list,
.read_inode = read_inode
};

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,264 @@
#ifndef UNSQUASHFS_H
#define UNSQUASHFS_H
/*
* Unsquash a squashfs filesystem. This is a highly compressed read only
* filesystem.
*
* Copyright (c) 2009, 2010, 2013, 2014, 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.
*
* unsquashfs.h
*/
#define TRUE 1
#define FALSE 0
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <sys/mman.h>
#include <utime.h>
#include <pwd.h>
#include <grp.h>
#include <time.h>
#include <regex.h>
#include <signal.h>
#include <pthread.h>
#include <math.h>
#include <sys/ioctl.h>
#include <sys/time.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 "error.h"
#define CALCULATE_HASH(start) (start & 0xffff)
/*
* Unified superblock containing fields for all superblocks
*/
struct super_block {
struct squashfs_super_block s;
/* fields only used by squashfs 3 and earlier layouts */
unsigned int no_uids;
unsigned int no_guids;
long long uid_start;
long long guid_start;
};
struct hash_table_entry {
long long start;
long long bytes;
struct hash_table_entry *next;
};
struct inode {
int blocks;
char *block_ptr;
long long data;
int fragment;
int frag_bytes;
gid_t gid;
int inode_number;
int mode;
int offset;
long long start;
char *symlink;
time_t time;
int type;
uid_t uid;
char sparse;
unsigned int xattr;
};
typedef struct squashfs_operations {
struct dir *(*opendir)(unsigned int block_start,
unsigned int offset, struct inode **i);
void (*read_fragment)(unsigned int fragment, long long *start_block,
int *size);
void (*read_block_list)(unsigned int *block_list, char *block_ptr,
int blocks);
struct inode *(*read_inode)(unsigned int start_block,
unsigned int offset);
} squashfs_operations;
struct test {
int mask;
int value;
int position;
char mode;
};
/* Cache status struct. Caches are used to keep
track of memory buffers passed between different threads */
struct cache {
int max_buffers;
int count;
int used;
int buffer_size;
int wait_free;
int wait_pending;
pthread_mutex_t mutex;
pthread_cond_t wait_for_free;
pthread_cond_t wait_for_pending;
struct cache_entry *free_list;
struct cache_entry *hash_table[65536];
};
/* struct describing a cache entry passed between threads */
struct cache_entry {
struct cache *cache;
long long block;
int size;
int used;
int error;
int pending;
struct cache_entry *hash_next;
struct cache_entry *hash_prev;
struct cache_entry *free_next;
struct cache_entry *free_prev;
char *data;
};
/* struct describing queues used to pass data between threads */
struct queue {
int size;
int readp;
int writep;
pthread_mutex_t mutex;
pthread_cond_t empty;
pthread_cond_t full;
void **data;
};
/* default size of fragment buffer in Mbytes */
#define FRAGMENT_BUFFER_DEFAULT 256
/* default size of data buffer in Mbytes */
#define DATA_BUFFER_DEFAULT 256
#define DIR_ENT_SIZE 16
struct dir_ent {
char name[SQUASHFS_NAME_LEN + 1];
unsigned int start_block;
unsigned int offset;
unsigned int type;
};
struct dir {
int dir_count;
int cur_entry;
unsigned int mode;
uid_t uid;
gid_t guid;
unsigned int mtime;
unsigned int xattr;
struct dir_ent *dirs;
};
struct file_entry {
int offset;
int size;
struct cache_entry *buffer;
};
struct squashfs_file {
int fd;
int blocks;
long long file_size;
int mode;
uid_t uid;
gid_t gid;
time_t time;
char *pathname;
char sparse;
unsigned int xattr;
};
struct path_entry {
char *name;
regex_t *preg;
struct pathname *paths;
};
struct pathname {
int names;
struct path_entry *name;
};
struct pathnames {
int count;
struct pathname *path[0];
};
#define PATHS_ALLOC_SIZE 10
/* globals */
extern struct super_block sBlk;
extern int swap;
extern struct hash_table_entry *inode_table_hash[65536],
*directory_table_hash[65536];
extern pthread_mutex_t screen_mutex;
extern int progress_enabled;
extern int inode_number;
extern int lookup_type[];
extern int fd;
extern int no_xattrs;
extern struct queue *to_reader, *to_inflate, *to_writer;
extern struct cache *fragment_cache, *data_cache;
/* unsquashfs.c */
extern void *read_inode_table(long long, long long);
extern void *read_directory_table(long long, long long);
extern long long lookup_entry(struct hash_table_entry **, long long);
extern int read_fs_bytes(int fd, long long, int, void *);
extern int read_block(int, long long, long long *, int, void *);
extern void enable_progress_bar();
extern void disable_progress_bar();
extern void dump_queue(struct queue *);
extern void dump_cache(struct cache *);
/* unsquash-1.c */
extern squashfs_operations *read_filesystem_tables_1();
/* unsquash-2.c */
extern squashfs_operations *read_filesystem_tables_2();
/* unsquash-3.c */
extern squashfs_operations *read_filesystem_tables_3();
/* unsquash-4.c */
extern squashfs_operations *read_filesystem_tables_4();
/* unsquash-123.c */
extern int read_ids(int, long long, long long, unsigned int **);
/* unsquash-34.c */
extern long long *alloc_index_table(int);
#endif

View file

@ -0,0 +1,145 @@
/*
* Create a squashfs filesystem. This is a highly compressed read only
* filesystem.
*
* Copyright (c) 2013
* 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.
*
* unsquashfs_info.c
*/
#include <pthread.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <signal.h>
#include <sys/time.h>
#include <stdio.h>
#include <math.h>
#include <stdarg.h>
#include <errno.h>
#include <stdlib.h>
#include <dirent.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include "squashfs_fs.h"
#include "unsquashfs.h"
#include "error.h"
static int silent = 0;
char *pathname = NULL;
pthread_t info_thread;
void disable_info()
{
if(pathname)
free(pathname);
pathname = NULL;
}
void update_info(char *name)
{
if(pathname)
free(pathname);
pathname = name;
}
void dump_state()
{
disable_progress_bar();
printf("Queue and cache status dump\n");
printf("===========================\n");
printf("file buffer read queue (main thread -> reader thread)\n");
dump_queue(to_reader);
printf("file buffer decompress queue (reader thread -> inflate"
" thread(s))\n");
dump_queue(to_inflate);
printf("file buffer write queue (main thread -> writer thread)\n");
dump_queue(to_writer);
printf("\nbuffer cache (uncompressed blocks and compressed blocks "
"'in flight')\n");
dump_cache(data_cache);
printf("fragment buffer cache (uncompressed frags and compressed"
" frags 'in flight')\n");
dump_cache(fragment_cache);
enable_progress_bar();
}
void *info_thrd(void *arg)
{
sigset_t sigmask;
struct timespec timespec = { .tv_sec = 1, .tv_nsec = 0 };
int sig, waiting = 0;
sigemptyset(&sigmask);
sigaddset(&sigmask, SIGQUIT);
sigaddset(&sigmask, SIGHUP);
while(1) {
if(waiting)
sig = sigtimedwait(&sigmask, NULL, &timespec);
else
sig = sigwaitinfo(&sigmask, NULL);
if(sig == -1) {
switch(errno) {
case EAGAIN:
/* interval timed out */
waiting = 0;
/* FALLTHROUGH */
case EINTR:
/* if waiting, the wait will be longer, but
that's OK */
continue;
default:
BAD_ERROR("sigtimedwait/sigwaitinfo failed "
"because %s\n", strerror(errno));
}
}
if(sig == SIGQUIT && !waiting) {
if(pathname)
INFO("%s\n", pathname);
/* set one second interval period, if ^\ received
within then, dump queue and cache status */
waiting = 1;
} else
dump_state();
}
}
void init_info()
{
pthread_create(&info_thread, NULL, info_thrd, NULL);
}

View file

@ -0,0 +1,30 @@
#ifndef UNSQUASHFS_INFO_H
#define UNSQUASHFS_INFO_H
/*
* Create a squashfs filesystem. This is a highly compressed read only
* filesystem.
*
* Copyright (c) 2013, 2014
* 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.
*
* unsquashfs_info.h
*/
extern void disable_info();
extern void update_info(char *);
extern void init_info();
#endif

View file

@ -0,0 +1,144 @@
/*
* Unsquash a squashfs filesystem. This is a highly compressed read only
* filesystem.
*
* Copyright (c) 2010, 2012, 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.
*
* unsquashfs_xattr.c
*/
#include "unsquashfs.h"
#include "xattr.h"
#include <sys/xattr.h>
#define NOSPACE_MAX 10
extern int root_process;
extern int user_xattrs;
extern int ignore_errors;
extern int strict_errors;
int write_xattr(char *pathname, unsigned int xattr)
{
unsigned int count;
struct xattr_list *xattr_list;
int i;
static int nonsuper_error = FALSE;
static int ignore_xattrs = FALSE;
static int nospace_error = 0;
int failed;
if(ignore_xattrs || xattr == SQUASHFS_INVALID_XATTR ||
sBlk.s.xattr_id_table_start == SQUASHFS_INVALID_BLK)
return TRUE;
xattr_list = get_xattr(xattr, &count, &failed);
if(failed)
EXIT_UNSQUASH_STRICT("write_xattr: Failed to read one or more xattrs for %s\n", pathname);
for(i = 0; i < count; i++) {
int prefix = xattr_list[i].type & SQUASHFS_XATTR_PREFIX_MASK;
if(ignore_xattrs || (user_xattrs && prefix != SQUASHFS_XATTR_USER))
continue;
if(root_process || prefix == SQUASHFS_XATTR_USER) {
int res = lsetxattr(pathname, xattr_list[i].full_name,
xattr_list[i].value, xattr_list[i].vsize, 0);
if(res == -1) {
if(errno == ENOTSUP) {
/*
* If the destination filesystem cannot
* suppport xattrs, print error, and
* disable xattr output as this error is
* unlikely to go away, and printing
* screenfulls of the same error message
* is rather annoying
*/
ERROR("write_xattr: failed to write "
"xattr %s for file %s because "
"extended attributes are not "
"supported by the destination "
"filesystem\n",
xattr_list[i].full_name,
pathname);
ERROR("Ignoring xattrs in "
"filesystem\n");
EXIT_UNSQUASH_STRICT("To avoid this error message, "
"specify -no-xattrs\n");
ignore_xattrs = TRUE;
} else if((errno == ENOSPC || errno == EDQUOT)
&& nospace_error < NOSPACE_MAX) {
/*
* Many filesystems like ext2/3/4 have
* limits on the amount of xattr
* data that can be stored per file
* (typically one block or 4K), so
* we shouldn't disable xattr ouput,
* as the error may be restriced to one
* file only. If we get a lot of these
* then suppress the error messsage
*/
EXIT_UNSQUASH_IGNORE("write_xattr: failed to write "
"xattr %s for file %s because "
"no extended attribute space "
"remaining (per file or "
"filesystem limit)\n",
xattr_list[i].full_name,
pathname);
if(++ nospace_error == NOSPACE_MAX)
ERROR("%d of these errors "
"printed, further error "
"messages of this type "
"are suppressed!\n",
NOSPACE_MAX);
} else
EXIT_UNSQUASH_IGNORE("write_xattr: failed to write "
"xattr %s for file %s because "
"%s\n", xattr_list[i].full_name,
pathname, strerror(errno));
failed = TRUE;
}
} else if(nonsuper_error == FALSE) {
/*
* if extract user xattrs only then
* error message is suppressed, if not
* print error, and then suppress further error
* messages to avoid possible screenfulls of the
* same error message!
*/
ERROR("write_xattr: could not write xattr %s "
"for file %s because you're not "
"superuser!\n",
xattr_list[i].full_name, pathname);
EXIT_UNSQUASH_STRICT("write_xattr: to avoid this error message, either"
" specify -user-xattrs, -no-xattrs, or run as "
"superuser!\n");
ERROR("Further error messages of this type are "
"suppressed!\n");
nonsuper_error = TRUE;
failed = TRUE;
}
}
free_xattr(xattr_list, count);
return !failed;
}

View file

@ -0,0 +1,719 @@
/*
* Create a squashfs filesystem. This is a highly compressed read only
* filesystem.
*
* Copyright (c) 2008, 2009, 2010, 2012, 2014, 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.
*
* xattr.c
*/
#define TRUE 1
#define FALSE 0
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <dirent.h>
#include <string.h>
#include <stdlib.h>
#include <sys/xattr.h>
#include "squashfs_fs.h"
#include "squashfs_swap.h"
#include "mksquashfs.h"
#include "xattr.h"
#include "error.h"
#include "progressbar.h"
/* compressed xattr table */
static char *xattr_table = NULL;
static unsigned int xattr_size = 0;
/* cached uncompressed xattr data */
static char *data_cache = NULL;
static int cache_bytes = 0, cache_size = 0;
/* cached uncompressed xattr id table */
static struct squashfs_xattr_id *xattr_id_table = NULL;
static int xattr_ids = 0;
/* saved compressed xattr table */
unsigned int sxattr_bytes = 0, stotal_xattr_bytes = 0;
/* saved cached uncompressed xattr data */
static char *sdata_cache = NULL;
static int scache_bytes = 0;
/* saved cached uncompressed xattr id table */
static int sxattr_ids = 0;
/* xattr hash table for value duplicate detection */
static struct xattr_list *dupl_value[65536];
/* xattr hash table for id duplicate detection */
static struct dupl_id *dupl_id[65536];
/* file system globals from mksquashfs.c */
extern int no_xattrs, noX;
extern long long bytes;
extern int fd;
extern unsigned int xattr_bytes, total_xattr_bytes;
/* helper functions from mksquashfs.c */
extern unsigned short get_checksum(char *, int, unsigned short);
extern void write_destination(int, long long, int, void *);
extern long long generic_write_table(int, void *, int, void *, int);
extern int mangle(char *, char *, int, int, int, int);
extern char *pathname(struct dir_ent *);
/* helper functions and definitions from read_xattrs.c */
extern int read_xattrs_from_disk(int, struct squashfs_super_block *, int, long long *);
extern struct xattr_list *get_xattr(int, unsigned int *, int *);
extern struct prefix prefix_table[];
static int get_prefix(struct xattr_list *xattr, char *name)
{
int i;
xattr->full_name = strdup(name);
for(i = 0; prefix_table[i].type != -1; i++) {
struct prefix *p = &prefix_table[i];
if(strncmp(xattr->full_name, p->prefix, strlen(p->prefix)) == 0)
break;
}
if(prefix_table[i].type != -1) {
xattr->name = xattr->full_name + strlen(prefix_table[i].prefix);
xattr->size = strlen(xattr->name);
}
return prefix_table[i].type;
}
static int read_xattrs_from_system(char *filename, struct xattr_list **xattrs)
{
ssize_t size, vsize;
char *xattr_names, *p;
int i;
struct xattr_list *xattr_list = NULL;
while(1) {
size = llistxattr(filename, NULL, 0);
if(size <= 0) {
if(size < 0 && errno != ENOTSUP) {
ERROR_START("llistxattr for %s failed in "
"read_attrs, because %s", filename,
strerror(errno));
ERROR_EXIT(". Ignoring");
}
return 0;
}
xattr_names = malloc(size);
if(xattr_names == NULL)
MEM_ERROR();
size = llistxattr(filename, xattr_names, size);
if(size < 0) {
free(xattr_names);
if(errno == ERANGE)
/* xattr list grew? Try again */
continue;
else {
ERROR_START("llistxattr for %s failed in "
"read_attrs, because %s", filename,
strerror(errno));
ERROR_EXIT(". Ignoring");
return 0;
}
}
break;
}
for(i = 0, p = xattr_names; p < xattr_names + size; i++) {
struct xattr_list *x = realloc(xattr_list, (i + 1) *
sizeof(struct xattr_list));
if(x == NULL)
MEM_ERROR();
xattr_list = x;
xattr_list[i].type = get_prefix(&xattr_list[i], p);
p += strlen(p) + 1;
if(xattr_list[i].type == -1) {
ERROR("Unrecognised xattr prefix %s\n",
xattr_list[i].full_name);
free(xattr_list[i].full_name);
i--;
continue;
}
while(1) {
vsize = lgetxattr(filename, xattr_list[i].full_name,
NULL, 0);
if(vsize < 0) {
ERROR_START("lgetxattr failed for %s in "
"read_attrs, because %s", filename,
strerror(errno));
ERROR_EXIT(". Ignoring");
free(xattr_list[i].full_name);
goto failed;
}
xattr_list[i].value = malloc(vsize);
if(xattr_list[i].value == NULL)
MEM_ERROR();
vsize = lgetxattr(filename, xattr_list[i].full_name,
xattr_list[i].value, vsize);
if(vsize < 0) {
free(xattr_list[i].value);
if(errno == ERANGE)
/* xattr grew? Try again */
continue;
else {
ERROR_START("lgetxattr failed for %s "
"in read_attrs, because %s",
filename, strerror(errno));
ERROR_EXIT(". Ignoring");
free(xattr_list[i].full_name);
goto failed;
}
}
break;
}
xattr_list[i].vsize = vsize;
TRACE("read_xattrs_from_system: filename %s, xattr name %s,"
" vsize %d\n", filename, xattr_list[i].full_name,
xattr_list[i].vsize);
}
free(xattr_names);
if(i > 0)
*xattrs = xattr_list;
else
free(xattr_list);
return i;
failed:
while(--i >= 0) {
free(xattr_list[i].full_name);
free(xattr_list[i].value);
}
free(xattr_list);
free(xattr_names);
return 0;
}
static int get_xattr_size(struct xattr_list *xattr)
{
int size = sizeof(struct squashfs_xattr_entry) +
sizeof(struct squashfs_xattr_val) + xattr->size;
if(xattr->type & XATTR_VALUE_OOL)
size += XATTR_VALUE_OOL_SIZE;
else
size += xattr->vsize;
return size;
}
static void *get_xattr_space(unsigned int req_size, long long *disk)
{
int data_space;
unsigned short c_byte;
/*
* Move and compress cached uncompressed data into xattr table.
*/
while(cache_bytes >= SQUASHFS_METADATA_SIZE) {
if((xattr_size - xattr_bytes) <
((SQUASHFS_METADATA_SIZE << 1)) + 2) {
xattr_table = realloc(xattr_table, xattr_size +
(SQUASHFS_METADATA_SIZE << 1) + 2);
if(xattr_table == NULL)
MEM_ERROR();
xattr_size += (SQUASHFS_METADATA_SIZE << 1) + 2;
}
c_byte = mangle(xattr_table + xattr_bytes + BLOCK_OFFSET,
data_cache, SQUASHFS_METADATA_SIZE,
SQUASHFS_METADATA_SIZE, noX, 0);
TRACE("Xattr block @ 0x%x, size %d\n", xattr_bytes, c_byte);
SQUASHFS_SWAP_SHORTS(&c_byte, xattr_table + xattr_bytes, 1);
xattr_bytes += SQUASHFS_COMPRESSED_SIZE(c_byte) + BLOCK_OFFSET;
memmove(data_cache, data_cache + SQUASHFS_METADATA_SIZE,
cache_bytes - SQUASHFS_METADATA_SIZE);
cache_bytes -= SQUASHFS_METADATA_SIZE;
}
/*
* Ensure there's enough space in the uncompressed data cache
*/
data_space = cache_size - cache_bytes;
if(data_space < req_size) {
int realloc_size = req_size - data_space;
data_cache = realloc(data_cache, cache_size +
realloc_size);
if(data_cache == NULL)
MEM_ERROR();
cache_size += realloc_size;
}
if(disk)
*disk = ((long long) xattr_bytes << 16) | cache_bytes;
cache_bytes += req_size;
return data_cache + cache_bytes - req_size;
}
static struct dupl_id *check_id_dupl(struct xattr_list *xattr_list, int xattrs)
{
struct dupl_id *entry;
int i;
unsigned short checksum = 0;
/* compute checksum over all xattrs */
for(i = 0; i < xattrs; i++) {
struct xattr_list *xattr = &xattr_list[i];
checksum = get_checksum(xattr->full_name,
strlen(xattr->full_name), checksum);
checksum = get_checksum(xattr->value,
xattr->vsize, checksum);
}
for(entry = dupl_id[checksum]; entry; entry = entry->next) {
if (entry->xattrs != xattrs)
continue;
for(i = 0; i < xattrs; i++) {
struct xattr_list *xattr = &xattr_list[i];
struct xattr_list *dup_xattr = &entry->xattr_list[i];
if(strcmp(xattr->full_name, dup_xattr->full_name))
break;
if(xattr->vsize != dup_xattr->vsize)
break;
if(memcmp(xattr->value, dup_xattr->value, xattr->vsize))
break;
}
if(i == xattrs)
break;
}
if(entry == NULL) {
/* no duplicate exists */
entry = malloc(sizeof(*entry));
if(entry == NULL)
MEM_ERROR();
entry->xattrs = xattrs;
entry->xattr_list = xattr_list;
entry->xattr_id = SQUASHFS_INVALID_XATTR;
entry->next = dupl_id[checksum];
dupl_id[checksum] = entry;
}
return entry;
}
static void check_value_dupl(struct xattr_list *xattr)
{
struct xattr_list *entry;
if(xattr->vsize < XATTR_VALUE_OOL_SIZE)
return;
/* Check if this is a duplicate of an existing value */
xattr->vchecksum = get_checksum(xattr->value, xattr->vsize, 0);
for(entry = dupl_value[xattr->vchecksum]; entry; entry = entry->vnext) {
if(entry->vsize != xattr->vsize)
continue;
if(memcmp(entry->value, xattr->value, xattr->vsize) == 0)
break;
}
if(entry == NULL) {
/*
* No duplicate exists, add to hash table, and mark as
* requiring writing
*/
xattr->vnext = dupl_value[xattr->vchecksum];
dupl_value[xattr->vchecksum] = xattr;
xattr->ool_value = SQUASHFS_INVALID_BLK;
} else {
/*
* Duplicate exists, make type XATTR_VALUE_OOL, and
* remember where the duplicate is
*/
xattr->type |= XATTR_VALUE_OOL;
xattr->ool_value = entry->ool_value;
/* on appending don't free duplicate values because the
* duplicate value already points to the non-duplicate value */
if(xattr->value != entry->value) {
free(xattr->value);
xattr->value = entry->value;
}
}
}
static int get_xattr_id(int xattrs, struct xattr_list *xattr_list,
long long xattr_disk, struct dupl_id *xattr_dupl)
{
int i, size = 0;
struct squashfs_xattr_id *xattr_id;
xattr_id_table = realloc(xattr_id_table, (xattr_ids + 1) *
sizeof(struct squashfs_xattr_id));
if(xattr_id_table == NULL)
MEM_ERROR();
/* get total uncompressed size of xattr data, needed for stat */
for(i = 0; i < xattrs; i++)
size += strlen(xattr_list[i].full_name) + 1 +
xattr_list[i].vsize;
xattr_id = &xattr_id_table[xattr_ids];
xattr_id->xattr = xattr_disk;
xattr_id->count = xattrs;
xattr_id->size = size;
/*
* keep track of total uncompressed xattr data, needed for mksquashfs
* file system summary
*/
total_xattr_bytes += size;
xattr_dupl->xattr_id = xattr_ids ++;
return xattr_dupl->xattr_id;
}
long long write_xattrs()
{
unsigned short c_byte;
int i, avail_bytes;
char *datap = data_cache;
long long start_bytes = bytes;
struct squashfs_xattr_table header;
if(xattr_ids == 0)
return SQUASHFS_INVALID_BLK;
/*
* Move and compress cached uncompressed data into xattr table.
*/
while(cache_bytes) {
if((xattr_size - xattr_bytes) <
((SQUASHFS_METADATA_SIZE << 1)) + 2) {
xattr_table = realloc(xattr_table, xattr_size +
(SQUASHFS_METADATA_SIZE << 1) + 2);
if(xattr_table == NULL)
MEM_ERROR();
xattr_size += (SQUASHFS_METADATA_SIZE << 1) + 2;
}
avail_bytes = cache_bytes > SQUASHFS_METADATA_SIZE ?
SQUASHFS_METADATA_SIZE : cache_bytes;
c_byte = mangle(xattr_table + xattr_bytes + BLOCK_OFFSET, datap,
avail_bytes, SQUASHFS_METADATA_SIZE, noX, 0);
TRACE("Xattr block @ 0x%x, size %d\n", xattr_bytes, c_byte);
SQUASHFS_SWAP_SHORTS(&c_byte, xattr_table + xattr_bytes, 1);
xattr_bytes += SQUASHFS_COMPRESSED_SIZE(c_byte) + BLOCK_OFFSET;
datap += avail_bytes;
cache_bytes -= avail_bytes;
}
/*
* Write compressed xattr table to file system
*/
write_destination(fd, bytes, xattr_bytes, xattr_table);
bytes += xattr_bytes;
/*
* Swap if necessary the xattr id table
*/
for(i = 0; i < xattr_ids; i++)
SQUASHFS_INSWAP_XATTR_ID(&xattr_id_table[i]);
header.xattr_ids = xattr_ids;
header.xattr_table_start = start_bytes;
SQUASHFS_INSWAP_XATTR_TABLE(&header);
return generic_write_table(xattr_ids * sizeof(struct squashfs_xattr_id),
xattr_id_table, sizeof(header), &header, noX);
}
int generate_xattrs(int xattrs, struct xattr_list *xattr_list)
{
int total_size, i;
int xattr_value_max;
void *xp;
long long xattr_disk;
struct dupl_id *xattr_dupl;
/*
* check if the file xattrs are a complete duplicate of a pre-existing
* id
*/
xattr_dupl = check_id_dupl(xattr_list, xattrs);
if(xattr_dupl->xattr_id != SQUASHFS_INVALID_XATTR)
return xattr_dupl->xattr_id;
/*
* Scan the xattr_list deciding which type to assign to each
* xattr. The choice is fairly straightforward, and depends on the
* size of each xattr name/value and the overall size of the
* resultant xattr list stored in the xattr metadata table.
*
* Choices are whether to store data inline or out of line.
*
* The overall goal is to optimise xattr scanning and lookup, and
* to enable the file system layout to scale from a couple of
* small xattr name/values to a large number of large xattr
* names/values without affecting performance. While hopefully
* enabling the common case of a couple of small xattr name/values
* to be stored efficiently
*
* Code repeatedly scans, doing the following
* move xattr data out of line if it exceeds
* xattr_value_max. Where xattr_value_max is
* initially XATTR_INLINE_MAX. If the final uncompressed
* xattr list is larger than XATTR_TARGET_MAX then more
* aggressively move xattr data out of line by repeatedly
* setting inline threshold to 1/2, then 1/4, 1/8 of
* XATTR_INLINE_MAX until target achieved or there's
* nothing left to move out of line
*/
xattr_value_max = XATTR_INLINE_MAX;
while(1) {
for(total_size = 0, i = 0; i < xattrs; i++) {
struct xattr_list *xattr = &xattr_list[i];
xattr->type &= XATTR_PREFIX_MASK; /* all inline */
if (xattr->vsize > xattr_value_max)
xattr->type |= XATTR_VALUE_OOL;
total_size += get_xattr_size(xattr);
}
/*
* If the total size of the uncompressed xattr list is <=
* XATTR_TARGET_MAX we're done
*/
if(total_size <= XATTR_TARGET_MAX)
break;
if(xattr_value_max == XATTR_VALUE_OOL_SIZE)
break;
/*
* Inline target not yet at minimum and so reduce it, and
* try again
*/
xattr_value_max /= 2;
if(xattr_value_max < XATTR_VALUE_OOL_SIZE)
xattr_value_max = XATTR_VALUE_OOL_SIZE;
}
/*
* Check xattr values for duplicates
*/
for(i = 0; i < xattrs; i++) {
check_value_dupl(&xattr_list[i]);
}
/*
* Add each out of line value to the file system xattr table
* if it doesn't already exist as a duplicate
*/
for(i = 0; i < xattrs; i++) {
struct xattr_list *xattr = &xattr_list[i];
if((xattr->type & XATTR_VALUE_OOL) &&
(xattr->ool_value == SQUASHFS_INVALID_BLK)) {
struct squashfs_xattr_val val;
int size = sizeof(val) + xattr->vsize;
xp = get_xattr_space(size, &xattr->ool_value);
val.vsize = xattr->vsize;
SQUASHFS_SWAP_XATTR_VAL(&val, xp);
memcpy(xp + sizeof(val), xattr->value, xattr->vsize);
}
}
/*
* Create xattr list and add to file system xattr table
*/
get_xattr_space(0, &xattr_disk);
for(i = 0; i < xattrs; i++) {
struct xattr_list *xattr = &xattr_list[i];
struct squashfs_xattr_entry entry;
struct squashfs_xattr_val val;
xp = get_xattr_space(sizeof(entry) + xattr->size, NULL);
entry.type = xattr->type;
entry.size = xattr->size;
SQUASHFS_SWAP_XATTR_ENTRY(&entry, xp);
memcpy(xp + sizeof(entry), xattr->name, xattr->size);
if(xattr->type & XATTR_VALUE_OOL) {
int size = sizeof(val) + XATTR_VALUE_OOL_SIZE;
xp = get_xattr_space(size, NULL);
val.vsize = XATTR_VALUE_OOL_SIZE;
SQUASHFS_SWAP_XATTR_VAL(&val, xp);
SQUASHFS_SWAP_LONG_LONGS(&xattr->ool_value, xp +
sizeof(val), 1);
} else {
int size = sizeof(val) + xattr->vsize;
xp = get_xattr_space(size, &xattr->ool_value);
val.vsize = xattr->vsize;
SQUASHFS_SWAP_XATTR_VAL(&val, xp);
memcpy(xp + sizeof(val), xattr->value, xattr->vsize);
}
}
/*
* Add to xattr id lookup table
*/
return get_xattr_id(xattrs, xattr_list, xattr_disk, xattr_dupl);
}
int read_xattrs(void *d)
{
struct dir_ent *dir_ent = d;
struct inode_info *inode = dir_ent->inode;
char *filename = pathname(dir_ent);
struct xattr_list *xattr_list;
int xattrs;
if(no_xattrs || IS_PSEUDO(inode) || inode->root_entry)
return SQUASHFS_INVALID_XATTR;
xattrs = read_xattrs_from_system(filename, &xattr_list);
if(xattrs == 0)
return SQUASHFS_INVALID_XATTR;
return generate_xattrs(xattrs, xattr_list);
}
/*
* Add the existing xattr ids and xattr metadata in the file system being
* appended to, to the in-memory xattr cache. This allows duplicate checking to
* take place against the xattrs already in the file system being appended to,
* and ensures the pre-existing xattrs are written out along with any new xattrs
*/
int get_xattrs(int fd, struct squashfs_super_block *sBlk)
{
int ids, res, i, id;
unsigned int count;
TRACE("get_xattrs\n");
res = read_xattrs_from_disk(fd, sBlk, FALSE, NULL);
if(res == SQUASHFS_INVALID_BLK || res == 0)
goto done;
ids = res;
/*
* for each xattr id read and construct its list of xattr
* name:value pairs, and add them to the in-memory xattr cache
*/
for(i = 0; i < ids; i++) {
struct xattr_list *xattr_list = get_xattr(i, &count, &res);
if(res) {
free_xattr(xattr_list, count);
return FALSE;
}
id = generate_xattrs(count, xattr_list);
/*
* Sanity check, the new xattr id should be the same as the
* xattr id in the original file system
*/
if(id != i) {
ERROR("BUG, different xattr_id in get_xattrs\n");
res = 0;
goto done;
}
}
done:
return res;
}
/*
* Save current state of xattrs, needed for restoring state in the event of an
* abort in appending
*/
void save_xattrs()
{
/* save the current state of the compressed xattr data */
sxattr_bytes = xattr_bytes;
stotal_xattr_bytes = total_xattr_bytes;
/*
* save the current state of the cached uncompressed xattr data.
* Note we have to save the contents of the data cache because future
* operations will delete the current contents
*/
sdata_cache = malloc(cache_bytes);
if(sdata_cache == NULL)
MEM_ERROR();
memcpy(sdata_cache, data_cache, cache_bytes);
scache_bytes = cache_bytes;
/* save the current state of the xattr id table */
sxattr_ids = xattr_ids;
}
/*
* Restore xattrs in the event of an abort in appending
*/
void restore_xattrs()
{
/* restore the state of the compressed xattr data */
xattr_bytes = sxattr_bytes;
total_xattr_bytes = stotal_xattr_bytes;
/* restore the state of the uncomoressed xattr data */
memcpy(data_cache, sdata_cache, scache_bytes);
cache_bytes = scache_bytes;
/* restore the state of the xattr id table */
xattr_ids = sxattr_ids;
}

View file

@ -0,0 +1,151 @@
#ifndef XATTR_H
#define XATTR_H
/*
* Create a squashfs filesystem. This is a highly compressed read only
* filesystem.
*
* Copyright (c) 2010, 2012, 2013, 2014, 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.
*
* xattr.h
*/
#define XATTR_VALUE_OOL SQUASHFS_XATTR_VALUE_OOL
#define XATTR_PREFIX_MASK SQUASHFS_XATTR_PREFIX_MASK
#define XATTR_VALUE_OOL_SIZE sizeof(long long)
/* maximum size of xattr value data that will be inlined */
#define XATTR_INLINE_MAX 128
/* the target size of an inode's xattr name:value list. If it
* exceeds this, then xattr value data will be successively out of lined
* until it meets the target */
#define XATTR_TARGET_MAX 65536
#define IS_XATTR(a) (a != SQUASHFS_INVALID_XATTR)
struct xattr_list {
char *name;
char *full_name;
int size;
int vsize;
void *value;
int type;
long long ool_value;
unsigned short vchecksum;
struct xattr_list *vnext;
};
struct dupl_id {
struct xattr_list *xattr_list;
int xattrs;
int xattr_id;
struct dupl_id *next;
};
struct prefix {
char *prefix;
int type;
};
extern int generate_xattrs(int, struct xattr_list *);
#ifdef XATTR_SUPPORT
extern int get_xattrs(int, struct squashfs_super_block *);
extern int read_xattrs(void *);
extern long long write_xattrs();
extern void save_xattrs();
extern void restore_xattrs();
extern unsigned int xattr_bytes, total_xattr_bytes;
extern int write_xattr(char *, unsigned int);
extern int read_xattrs_from_disk(int, struct squashfs_super_block *, int, long long *);
extern struct xattr_list *get_xattr(int, unsigned int *, int *);
extern void free_xattr(struct xattr_list *, int);
#else
static inline int get_xattrs(int fd, struct squashfs_super_block *sBlk)
{
if(sBlk->xattr_id_table_start != SQUASHFS_INVALID_BLK) {
fprintf(stderr, "Xattrs in filesystem! These are not "
"supported on this version of Squashfs\n");
return 0;
} else
return SQUASHFS_INVALID_BLK;
}
static inline int read_xattrs(void *dir_ent)
{
return SQUASHFS_INVALID_XATTR;
}
static inline long long write_xattrs()
{
return SQUASHFS_INVALID_BLK;
}
static inline void save_xattrs()
{
}
static inline void restore_xattrs()
{
}
static inline int write_xattr(char *pathname, unsigned int xattr)
{
return 0;
}
static inline int read_xattrs_from_disk(int fd, struct squashfs_super_block *sBlk, int flag, long long *table_start)
{
if(sBlk->xattr_id_table_start != SQUASHFS_INVALID_BLK) {
fprintf(stderr, "Xattrs in filesystem! These are not "
"supported on this version of Squashfs\n");
return 0;
} else
return SQUASHFS_INVALID_BLK;
}
static inline struct xattr_list *get_xattr(int i, unsigned int *count, int j)
{
return NULL;
}
#endif
#ifdef XATTR_SUPPORT
#ifdef XATTR_DEFAULT
#define NOXOPT_STR
#define XOPT_STR " (default)"
#define XATTR_DEF 0
#else
#define NOXOPT_STR " (default)"
#define XOPT_STR
#define XATTR_DEF 1
#endif
#else
#define NOXOPT_STR " (default)"
#define XOPT_STR " (unsupported)"
#define XATTR_DEF 1
#endif
#endif

View file

@ -0,0 +1,540 @@
/*
* Copyright (c) 2010, 2011, 2012, 2013
* 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.
*
* xz_wrapper.c
*
* Support for XZ (LZMA2) compression using XZ Utils liblzma
* http://tukaani.org/xz/
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <lzma.h>
#include "squashfs_fs.h"
#include "xz_wrapper.h"
#include "compressor.h"
static struct bcj bcj[] = {
{ "x86", LZMA_FILTER_X86, 0 },
{ "powerpc", LZMA_FILTER_POWERPC, 0 },
{ "ia64", LZMA_FILTER_IA64, 0 },
{ "arm", LZMA_FILTER_ARM, 0 },
{ "armthumb", LZMA_FILTER_ARMTHUMB, 0 },
{ "sparc", LZMA_FILTER_SPARC, 0 },
{ NULL, LZMA_VLI_UNKNOWN, 0 }
};
static int filter_count = 1;
static int dictionary_size = 0;
static float dictionary_percent = 0;
/*
* This function is called by the options parsing code in mksquashfs.c
* to parse any -X compressor option.
*
* Two specific options are supported:
* -Xbcj
* -Xdict-size
*
* This function returns:
* >=0 (number of additional args parsed) on success
* -1 if the option was unrecognised, or
* -2 if the option was recognised, but otherwise bad in
* some way (e.g. invalid parameter)
*
* Note: this function sets internal compressor state, but does not
* pass back the results of the parsing other than success/failure.
* The xz_dump_options() function is called later to get the options in
* a format suitable for writing to the filesystem.
*/
static int xz_options(char *argv[], int argc)
{
int i;
char *name;
if(strcmp(argv[0], "-Xbcj") == 0) {
if(argc < 2) {
fprintf(stderr, "xz: -Xbcj missing filter\n");
goto failed;
}
name = argv[1];
while(name[0] != '\0') {
for(i = 0; bcj[i].name; i++) {
int n = strlen(bcj[i].name);
if((strncmp(name, bcj[i].name, n) == 0) &&
(name[n] == '\0' ||
name[n] == ',')) {
if(bcj[i].selected == 0) {
bcj[i].selected = 1;
filter_count++;
}
name += name[n] == ',' ? n + 1 : n;
break;
}
}
if(bcj[i].name == NULL) {
fprintf(stderr, "xz: -Xbcj unrecognised "
"filter\n");
goto failed;
}
}
return 1;
} else if(strcmp(argv[0], "-Xdict-size") == 0) {
char *b;
float size;
if(argc < 2) {
fprintf(stderr, "xz: -Xdict-size missing dict-size\n");
goto failed;
}
size = strtof(argv[1], &b);
if(*b == '%') {
if(size <= 0 || size > 100) {
fprintf(stderr, "xz: -Xdict-size percentage "
"should be 0 < dict-size <= 100\n");
goto failed;
}
dictionary_percent = size;
dictionary_size = 0;
} else {
if((float) ((int) size) != size) {
fprintf(stderr, "xz: -Xdict-size can't be "
"fractional unless a percentage of the"
" block size\n");
goto failed;
}
dictionary_percent = 0;
dictionary_size = (int) size;
if(*b == 'k' || *b == 'K')
dictionary_size *= 1024;
else if(*b == 'm' || *b == 'M')
dictionary_size *= 1024 * 1024;
else if(*b != '\0') {
fprintf(stderr, "xz: -Xdict-size invalid "
"dict-size\n");
goto failed;
}
}
return 1;
}
return -1;
failed:
return -2;
}
/*
* This function is called after all options have been parsed.
* It is used to do post-processing on the compressor options using
* values that were not expected to be known at option parse time.
*
* In this case block_size may not be known until after -Xdict-size has
* been processed (in the case where -b is specified after -Xdict-size)
*
* This function returns 0 on successful post processing, or
* -1 on error
*/
static int xz_options_post(int block_size)
{
/*
* if -Xdict-size has been specified use this to compute the datablock
* dictionary size
*/
if(dictionary_size || dictionary_percent) {
int n;
if(dictionary_size) {
if(dictionary_size > block_size) {
fprintf(stderr, "xz: -Xdict-size is larger than"
" block_size\n");
goto failed;
}
} else
dictionary_size = block_size * dictionary_percent / 100;
if(dictionary_size < 8192) {
fprintf(stderr, "xz: -Xdict-size should be 8192 bytes "
"or larger\n");
goto failed;
}
/*
* dictionary_size must be storable in xz header as either
* 2^n or as 2^n+2^(n+1)
*/
n = ffs(dictionary_size) - 1;
if(dictionary_size != (1 << n) &&
dictionary_size != ((1 << n) + (1 << (n + 1)))) {
fprintf(stderr, "xz: -Xdict-size is an unsupported "
"value, dict-size must be storable in xz "
"header\n");
fprintf(stderr, "as either 2^n or as 2^n+2^(n+1). "
"Example dict-sizes are 75%%, 50%%, 37.5%%, "
"25%%,\n");
fprintf(stderr, "or 32K, 16K, 8K etc.\n");
goto failed;
}
} else
/* No -Xdict-size specified, use defaults */
dictionary_size = block_size;
return 0;
failed:
return -1;
}
/*
* This function is called by mksquashfs to dump the parsed
* compressor options in a format suitable for writing to the
* compressor options field in the filesystem (stored immediately
* after the superblock).
*
* This function returns a pointer to the compression options structure
* to be stored (and the size), or NULL if there are no compression
* options
*/
static void *xz_dump_options(int block_size, int *size)
{
static struct comp_opts comp_opts;
int flags = 0, i;
/*
* don't store compressor specific options in file system if the
* default options are being used - no compressor options in the
* file system means the default options are always assumed
*
* Defaults are:
* metadata dictionary size: SQUASHFS_METADATA_SIZE
* datablock dictionary size: block_size
* 1 filter
*/
if(dictionary_size == block_size && filter_count == 1)
return NULL;
for(i = 0; bcj[i].name; i++)
flags |= bcj[i].selected << i;
comp_opts.dictionary_size = dictionary_size;
comp_opts.flags = flags;
SQUASHFS_INSWAP_COMP_OPTS(&comp_opts);
*size = sizeof(comp_opts);
return &comp_opts;
}
/*
* This function is a helper specifically for the append mode of
* mksquashfs. Its purpose is to set the internal compressor state
* to the stored compressor options in the passed compressor options
* structure.
*
* In effect this function sets up the compressor options
* to the same state they were when the filesystem was originally
* generated, this is to ensure on appending, the compressor uses
* the same compression options that were used to generate the
* original filesystem.
*
* Note, even if there are no compressor options, this function is still
* called with an empty compressor structure (size == 0), to explicitly
* set the default options, this is to ensure any user supplied
* -X options on the appending mksquashfs command line are over-ridden
*
* This function returns 0 on sucessful extraction of options, and
* -1 on error
*/
static int xz_extract_options(int block_size, void *buffer, int size)
{
struct comp_opts *comp_opts = buffer;
int flags, i, n;
if(size == 0) {
/* set defaults */
dictionary_size = block_size;
flags = 0;
} else {
/* check passed comp opts struct is of the correct length */
if(size != sizeof(struct comp_opts))
goto failed;
SQUASHFS_INSWAP_COMP_OPTS(comp_opts);
dictionary_size = comp_opts->dictionary_size;
flags = comp_opts->flags;
/*
* check that the dictionary size seems correct - the dictionary
* size should 2^n or 2^n+2^(n+1)
*/
n = ffs(dictionary_size) - 1;
if(dictionary_size != (1 << n) &&
dictionary_size != ((1 << n) + (1 << (n + 1))))
goto failed;
}
filter_count = 1;
for(i = 0; bcj[i].name; i++) {
if((flags >> i) & 1) {
bcj[i].selected = 1;
filter_count ++;
} else
bcj[i].selected = 0;
}
return 0;
failed:
fprintf(stderr, "xz: error reading stored compressor options from "
"filesystem!\n");
return -1;
}
static void xz_display_options(void *buffer, int size)
{
struct comp_opts *comp_opts = buffer;
int dictionary_size, flags, printed;
int i, n;
/* check passed comp opts struct is of the correct length */
if(size != sizeof(struct comp_opts))
goto failed;
SQUASHFS_INSWAP_COMP_OPTS(comp_opts);
dictionary_size = comp_opts->dictionary_size;
flags = comp_opts->flags;
/*
* check that the dictionary size seems correct - the dictionary
* size should 2^n or 2^n+2^(n+1)
*/
n = ffs(dictionary_size) - 1;
if(dictionary_size != (1 << n) &&
dictionary_size != ((1 << n) + (1 << (n + 1))))
goto failed;
printf("\tDictionary size %d\n", dictionary_size);
printed = 0;
for(i = 0; bcj[i].name; i++) {
if((flags >> i) & 1) {
if(printed)
printf(", ");
else
printf("\tFilters selected: ");
printf("%s", bcj[i].name);
printed = 1;
}
}
if(!printed)
printf("\tNo filters specified\n");
else
printf("\n");
return;
failed:
fprintf(stderr, "xz: error reading stored compressor options from "
"filesystem!\n");
}
/*
* This function is called by mksquashfs to initialise the
* compressor, before compress() is called.
*
* This function returns 0 on success, and
* -1 on error
*/
static int xz_init(void **strm, int block_size, int datablock)
{
int i, j, filters = datablock ? filter_count : 1;
struct filter *filter = malloc(filters * sizeof(struct filter));
struct xz_stream *stream;
if(filter == NULL)
goto failed;
stream = *strm = malloc(sizeof(struct xz_stream));
if(stream == NULL)
goto failed2;
stream->filter = filter;
stream->filters = filters;
memset(filter, 0, filters * sizeof(struct filter));
stream->dictionary_size = datablock ? dictionary_size :
SQUASHFS_METADATA_SIZE;
filter[0].filter[0].id = LZMA_FILTER_LZMA2;
filter[0].filter[0].options = &stream->opt;
filter[0].filter[1].id = LZMA_VLI_UNKNOWN;
for(i = 0, j = 1; datablock && bcj[i].name; i++) {
if(bcj[i].selected) {
filter[j].buffer = malloc(block_size);
if(filter[j].buffer == NULL)
goto failed3;
filter[j].filter[0].id = bcj[i].id;
filter[j].filter[1].id = LZMA_FILTER_LZMA2;
filter[j].filter[1].options = &stream->opt;
filter[j].filter[2].id = LZMA_VLI_UNKNOWN;
j++;
}
}
return 0;
failed3:
for(i = 1; i < filters; i++)
free(filter[i].buffer);
free(stream);
failed2:
free(filter);
failed:
return -1;
}
static int xz_compress(void *strm, void *dest, void *src, int size,
int block_size, int *error)
{
int i;
lzma_ret res = 0;
struct xz_stream *stream = strm;
struct filter *selected = NULL;
stream->filter[0].buffer = dest;
for(i = 0; i < stream->filters; i++) {
struct filter *filter = &stream->filter[i];
if(lzma_lzma_preset(&stream->opt, LZMA_PRESET_DEFAULT))
goto failed;
stream->opt.dict_size = stream->dictionary_size;
filter->length = 0;
res = lzma_stream_buffer_encode(filter->filter,
LZMA_CHECK_CRC32, NULL, src, size, filter->buffer,
&filter->length, block_size);
if(res == LZMA_OK) {
if(!selected || selected->length > filter->length)
selected = filter;
} else if(res != LZMA_BUF_ERROR)
goto failed;
}
if(!selected)
/*
* Output buffer overflow. Return out of buffer space
*/
return 0;
if(selected->buffer != dest)
memcpy(dest, selected->buffer, selected->length);
return (int) selected->length;
failed:
/*
* All other errors return failure, with the compressor
* specific error code in *error
*/
*error = res;
return -1;
}
static int xz_uncompress(void *dest, void *src, int size, int outsize,
int *error)
{
size_t src_pos = 0;
size_t dest_pos = 0;
uint64_t memlimit = MEMLIMIT;
lzma_ret res = lzma_stream_buffer_decode(&memlimit, 0, NULL,
src, &src_pos, size, dest, &dest_pos, outsize);
if(res == LZMA_OK && size == (int) src_pos)
return (int) dest_pos;
else {
*error = res;
return -1;
}
}
static void xz_usage()
{
fprintf(stderr, "\t -Xbcj filter1,filter2,...,filterN\n");
fprintf(stderr, "\t\tCompress using filter1,filter2,...,filterN in");
fprintf(stderr, " turn\n\t\t(in addition to no filter), and choose");
fprintf(stderr, " the best compression.\n");
fprintf(stderr, "\t\tAvailable filters: x86, arm, armthumb,");
fprintf(stderr, " powerpc, sparc, ia64\n");
fprintf(stderr, "\t -Xdict-size <dict-size>\n");
fprintf(stderr, "\t\tUse <dict-size> as the XZ dictionary size. The");
fprintf(stderr, " dictionary size\n\t\tcan be specified as a");
fprintf(stderr, " percentage of the block size, or as an\n\t\t");
fprintf(stderr, "absolute value. The dictionary size must be less");
fprintf(stderr, " than or equal\n\t\tto the block size and 8192 bytes");
fprintf(stderr, " or larger. It must also be\n\t\tstorable in the xz");
fprintf(stderr, " header as either 2^n or as 2^n+2^(n+1).\n\t\t");
fprintf(stderr, "Example dict-sizes are 75%%, 50%%, 37.5%%, 25%%, or");
fprintf(stderr, " 32K, 16K, 8K\n\t\tetc.\n");
}
struct compressor xz_comp_ops = {
.init = xz_init,
.compress = xz_compress,
.uncompress = xz_uncompress,
.options = xz_options,
.options_post = xz_options_post,
.dump_options = xz_dump_options,
.extract_options = xz_extract_options,
.display_options = xz_display_options,
.usage = xz_usage,
.id = XZ_COMPRESSION,
.name = "xz",
.supported = 1
};

View file

@ -0,0 +1,71 @@
#ifndef XZ_WRAPPER_H
#define XZ_WRAPPER_H
/*
* Squashfs
*
* Copyright (c) 2010
* 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.
*
* xz_wrapper.h
*
*/
#ifndef linux
#define __BYTE_ORDER BYTE_ORDER
#define __BIG_ENDIAN BIG_ENDIAN
#define __LITTLE_ENDIAN LITTLE_ENDIAN
#else
#include <endian.h>
#endif
#if __BYTE_ORDER == __BIG_ENDIAN
extern unsigned int inswap_le32(unsigned int);
#define SQUASHFS_INSWAP_COMP_OPTS(s) { \
(s)->dictionary_size = inswap_le32((s)->dictionary_size); \
(s)->flags = inswap_le32((s)->flags); \
}
#else
#define SQUASHFS_INSWAP_COMP_OPTS(s)
#endif
#define MEMLIMIT (32 * 1024 * 1024)
struct bcj {
char *name;
lzma_vli id;
int selected;
};
struct filter {
void *buffer;
lzma_filter filter[3];
size_t length;
};
struct xz_stream {
struct filter *filter;
int filters;
int dictionary_size;
lzma_options_lzma opt;
};
struct comp_opts {
int dictionary_size;
int flags;
};
#endif

View file

@ -0,0 +1,199 @@
/*
* Copyright (c) 2017
* 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.
*
* zstd_wrapper.c
*
* Support for ZSTD compression http://zstd.net
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <zstd.h>
#include <zstd_errors.h>
#include "squashfs_fs.h"
#include "zstd_wrapper.h"
#include "compressor.h"
static int compression_level = ZSTD_DEFAULT_COMPRESSION_LEVEL;
/*
* This function is called by the options parsing code in mksquashfs.c
* to parse any -X compressor option.
*
* This function returns:
* >=0 (number of additional args parsed) on success
* -1 if the option was unrecognised, or
* -2 if the option was recognised, but otherwise bad in
* some way (e.g. invalid parameter)
*
* Note: this function sets internal compressor state, but does not
* pass back the results of the parsing other than success/failure.
* The zstd_dump_options() function is called later to get the options in
* a format suitable for writing to the filesystem.
*/
static int zstd_options(char *argv[], int argc)
{
return 1;
}
/*
* This function is called by mksquashfs to dump the parsed
* compressor options in a format suitable for writing to the
* compressor options field in the filesystem (stored immediately
* after the superblock).
*
* This function returns a pointer to the compression options structure
* to be stored (and the size), or NULL if there are no compression
* options.
*/
static void *zstd_dump_options(int block_size, int *size)
{
return NULL;
}
/*
* This function is a helper specifically for the append mode of
* mksquashfs. Its purpose is to set the internal compressor state
* to the stored compressor options in the passed compressor options
* structure.
*
* In effect this function sets up the compressor options
* to the same state they were when the filesystem was originally
* generated, this is to ensure on appending, the compressor uses
* the same compression options that were used to generate the
* original filesystem.
*
* Note, even if there are no compressor options, this function is still
* called with an empty compressor structure (size == 0), to explicitly
* set the default options, this is to ensure any user supplied
* -X options on the appending mksquashfs command line are over-ridden.
*
* This function returns 0 on sucessful extraction of options, and -1 on error.
*/
static int zstd_extract_options(int block_size, void *buffer, int size)
{
struct zstd_comp_opts *comp_opts = buffer;
if (size == 0) {
/* Set default values */
compression_level = ZSTD_DEFAULT_COMPRESSION_LEVEL;
return 0;
}
/* we expect a comp_opts structure of sufficient size to be present */
if (size < sizeof(*comp_opts))
goto failed;
SQUASHFS_INSWAP_COMP_OPTS(comp_opts);
if (comp_opts->compression_level < 1) {
fprintf(stderr, "zstd: bad compression level in compression "
"options structure\n");
goto failed;
}
compression_level = comp_opts->compression_level;
return 0;
failed:
fprintf(stderr, "zstd: error reading stored compressor options from "
"filesystem!\n");
return -1;
}
static void zstd_display_options(void *buffer, int size)
{
struct zstd_comp_opts *comp_opts = buffer;
/* we expect a comp_opts structure of sufficient size to be present */
if (size < sizeof(*comp_opts))
goto failed;
SQUASHFS_INSWAP_COMP_OPTS(comp_opts);
if (comp_opts->compression_level < 1) {
fprintf(stderr, "zstd: bad compression level in compression "
"options structure\n");
goto failed;
}
printf("\tcompression-level %d\n", comp_opts->compression_level);
return;
failed:
fprintf(stderr, "zstd: error reading stored compressor options from "
"filesystem!\n");
}
/*
* This function is called by mksquashfs to initialise the
* compressor, before compress() is called.
*
* This function returns 0 on success, and -1 on error.
*/
static int zstd_init(void **strm, int block_size, int datablock)
{
return 0;
}
static int zstd_compress(void *strm, void *dest, void *src, int size,
int block_size, int *error)
{
(void)strm;
(void)dest;
(void)src;
(void)size;
(void)block_size;
(void)error;
return 0;
}
static int zstd_uncompress(void *dest, void *src, int size, int outsize,
int *error)
{
const size_t res = ZSTD_decompress(dest, outsize, src, size);
if (ZSTD_isError(res)) {
fprintf(stderr, "\t%d %d\n", outsize, size);
*error = (int)ZSTD_getErrorCode(res);
return -1;
}
return (int)res;
}
static void zstd_usage(void)
{
fprintf(stderr, "\t -Xcompression-level <compression-level>\n");
}
struct compressor zstd_comp_ops = {
.init = zstd_init,
.compress = zstd_compress,
.uncompress = zstd_uncompress,
.options = zstd_options,
.dump_options = zstd_dump_options,
.extract_options = zstd_extract_options,
.display_options = zstd_display_options,
.usage = zstd_usage,
.id = ZSTD_COMPRESSION,
.name = "zstd",
.supported = 1
};

View file

@ -0,0 +1,48 @@
#ifndef ZSTD_WRAPPER_H
#define ZSTD_WRAPPER_H
/*
* Squashfs
*
* Copyright (c) 2017
* 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.
*
* zstd_wrapper.h
*
*/
#ifndef linux
#define __BYTE_ORDER BYTE_ORDER
#define __BIG_ENDIAN BIG_ENDIAN
#define __LITTLE_ENDIAN LITTLE_ENDIAN
#else
#include <endian.h>
#endif
#if __BYTE_ORDER == __BIG_ENDIAN
extern unsigned int inswap_le16(unsigned short);
extern unsigned int inswap_le32(unsigned int);
#define SQUASHFS_INSWAP_COMP_OPTS(s) { \
(s)->compression_level = inswap_le32((s)->compression_level); \
}
#else
#define SQUASHFS_INSWAP_COMP_OPTS(s)
#endif
/* Default compression */
#define ZSTD_DEFAULT_COMPRESSION_LEVEL 15
struct zstd_comp_opts {
int compression_level;
};
#endif