From cdccc69c62579b0c13b35fad0728079652b8f3c9 Mon Sep 17 00:00:00 2001 From: David Oberhollenzer Date: Tue, 31 Jan 2023 11:21:30 +0100 Subject: Move library source into src sub-directory Signed-off-by: David Oberhollenzer --- bin/gensquashfs/Makemodule.am | 13 +- bin/gensquashfs/dirscan_xattr.c | 220 --------- bin/gensquashfs/filemap_xattr.c | 254 ----------- bin/gensquashfs/fstree_from_dir.c | 493 --------------------- bin/gensquashfs/fstree_from_file.c | 591 ------------------------- bin/gensquashfs/mkfs.c | 215 --------- bin/gensquashfs/mkfs.h | 137 ------ bin/gensquashfs/options.c | 383 ---------------- bin/gensquashfs/selinux.c | 78 ---- bin/gensquashfs/sort_by_file.c | 368 --------------- bin/gensquashfs/src/dirscan_xattr.c | 220 +++++++++ bin/gensquashfs/src/filemap_xattr.c | 254 +++++++++++ bin/gensquashfs/src/fstree_from_dir.c | 493 +++++++++++++++++++++ bin/gensquashfs/src/fstree_from_file.c | 591 +++++++++++++++++++++++++ bin/gensquashfs/src/mkfs.c | 215 +++++++++ bin/gensquashfs/src/mkfs.h | 137 ++++++ bin/gensquashfs/src/options.c | 383 ++++++++++++++++ bin/gensquashfs/src/selinux.c | 78 ++++ bin/gensquashfs/src/sort_by_file.c | 368 +++++++++++++++ bin/rdsquashfs/Makemodule.am | 10 +- bin/rdsquashfs/describe.c | 139 ------ bin/rdsquashfs/dump_xattrs.c | 120 ----- bin/rdsquashfs/fill_files.c | 186 -------- bin/rdsquashfs/list_files.c | 158 ------- bin/rdsquashfs/options.c | 226 ---------- bin/rdsquashfs/rdsquashfs.c | 275 ------------ bin/rdsquashfs/rdsquashfs.h | 82 ---- bin/rdsquashfs/restore_fstree.c | 336 -------------- bin/rdsquashfs/src/describe.c | 139 ++++++ bin/rdsquashfs/src/dump_xattrs.c | 120 +++++ bin/rdsquashfs/src/fill_files.c | 186 ++++++++ bin/rdsquashfs/src/list_files.c | 158 +++++++ bin/rdsquashfs/src/options.c | 226 ++++++++++ bin/rdsquashfs/src/rdsquashfs.c | 275 ++++++++++++ bin/rdsquashfs/src/rdsquashfs.h | 82 ++++ bin/rdsquashfs/src/restore_fstree.c | 336 ++++++++++++++ bin/rdsquashfs/src/stat.c | 187 ++++++++ bin/rdsquashfs/stat.c | 187 -------- bin/sqfs2tar/Makemodule.am | 6 +- bin/sqfs2tar/options.c | 212 --------- bin/sqfs2tar/sqfs2tar.c | 274 ------------ bin/sqfs2tar/sqfs2tar.h | 56 --- bin/sqfs2tar/src/options.c | 212 +++++++++ bin/sqfs2tar/src/sqfs2tar.c | 274 ++++++++++++ bin/sqfs2tar/src/sqfs2tar.h | 56 +++ bin/sqfs2tar/src/write_tree.c | 209 +++++++++ bin/sqfs2tar/src/xattr.c | 91 ++++ bin/sqfs2tar/write_tree.c | 209 --------- bin/sqfs2tar/xattr.c | 91 ---- bin/sqfsdiff/Makemodule.am | 10 +- bin/sqfsdiff/compare_dir.c | 94 ---- bin/sqfsdiff/compare_files.c | 72 --- bin/sqfsdiff/extract.c | 58 --- bin/sqfsdiff/node_compare.c | 206 --------- bin/sqfsdiff/options.c | 131 ------ bin/sqfsdiff/sqfsdiff.c | 167 ------- bin/sqfsdiff/sqfsdiff.h | 73 --- bin/sqfsdiff/src/compare_dir.c | 94 ++++ bin/sqfsdiff/src/compare_files.c | 72 +++ bin/sqfsdiff/src/extract.c | 58 +++ bin/sqfsdiff/src/node_compare.c | 206 +++++++++ bin/sqfsdiff/src/options.c | 131 ++++++ bin/sqfsdiff/src/sqfsdiff.c | 167 +++++++ bin/sqfsdiff/src/sqfsdiff.h | 73 +++ bin/sqfsdiff/src/super.c | 125 ++++++ bin/sqfsdiff/src/util.c | 27 ++ bin/sqfsdiff/super.c | 125 ------ bin/sqfsdiff/util.c | 27 -- bin/tar2sqfs/Makemodule.am | 4 +- bin/tar2sqfs/options.c | 257 ----------- bin/tar2sqfs/process_tarball.c | 346 --------------- bin/tar2sqfs/src/options.c | 257 +++++++++++ bin/tar2sqfs/src/process_tarball.c | 346 +++++++++++++++ bin/tar2sqfs/src/tar2sqfs.c | 104 +++++ bin/tar2sqfs/src/tar2sqfs.h | 39 ++ bin/tar2sqfs/tar2sqfs.c | 104 ----- bin/tar2sqfs/tar2sqfs.h | 39 -- lib/common/Makemodule.am | 22 +- lib/common/comp_lzo.c | 285 ------------ lib/common/comp_opt.c | 378 ---------------- lib/common/compress.c | 88 ---- lib/common/data_reader_dump.c | 65 --- lib/common/data_writer.c | 54 --- lib/common/data_writer_ostream.c | 91 ---- lib/common/hardlink.c | 101 ----- lib/common/parse_size.c | 79 ---- lib/common/perror.c | 79 ---- lib/common/print_size.c | 38 -- lib/common/print_version.c | 25 -- lib/common/src/comp_lzo.c | 285 ++++++++++++ lib/common/src/comp_opt.c | 378 ++++++++++++++++ lib/common/src/compress.c | 88 ++++ lib/common/src/data_reader_dump.c | 65 +++ lib/common/src/data_writer.c | 54 +++ lib/common/src/data_writer_ostream.c | 91 ++++ lib/common/src/hardlink.c | 101 +++++ lib/common/src/parse_size.c | 79 ++++ lib/common/src/perror.c | 79 ++++ lib/common/src/print_size.c | 38 ++ lib/common/src/print_version.c | 25 ++ lib/common/src/writer/cleanup.c | 39 ++ lib/common/src/writer/finish.c | 176 ++++++++ lib/common/src/writer/init.c | 218 +++++++++ lib/common/src/writer/serialize_fstree.c | 202 +++++++++ lib/common/writer/cleanup.c | 39 -- lib/common/writer/finish.c | 176 -------- lib/common/writer/init.c | 218 --------- lib/common/writer/serialize_fstree.c | 202 --------- lib/compat/Makemodule.am | 18 +- lib/compat/chdir.c | 34 -- lib/compat/fnmatch.c | 305 ------------- lib/compat/getopt.c | 96 ---- lib/compat/getopt_long.c | 95 ---- lib/compat/getsubopt.c | 27 -- lib/compat/mockups.c | 51 --- lib/compat/path_to_windows.c | 43 -- lib/compat/src/chdir.c | 34 ++ lib/compat/src/fnmatch.c | 305 +++++++++++++ lib/compat/src/getopt.c | 96 ++++ lib/compat/src/getopt_long.c | 95 ++++ lib/compat/src/getsubopt.c | 27 ++ lib/compat/src/mockups.c | 51 +++ lib/compat/src/path_to_windows.c | 43 ++ lib/compat/src/strchrnul.c | 18 + lib/compat/src/strndup.c | 31 ++ lib/compat/src/w32_perror.c | 33 ++ lib/compat/src/w32_stdio.c | 125 ++++++ lib/compat/src/w32_wmain.c | 83 ++++ lib/compat/strchrnul.c | 18 - lib/compat/strndup.c | 31 -- lib/compat/w32_perror.c | 33 -- lib/compat/w32_stdio.c | 125 ------ lib/compat/w32_wmain.c | 83 ---- lib/fstree/Makemodule.am | 10 +- lib/fstree/add_by_path.c | 54 --- lib/fstree/fstree.c | 135 ------ lib/fstree/get_by_path.c | 76 ---- lib/fstree/get_path.c | 43 -- lib/fstree/hardlink.c | 76 ---- lib/fstree/mknode.c | 101 ----- lib/fstree/post_process.c | 221 --------- lib/fstree/src/add_by_path.c | 54 +++ lib/fstree/src/fstree.c | 135 ++++++ lib/fstree/src/get_by_path.c | 76 ++++ lib/fstree/src/get_path.c | 43 ++ lib/fstree/src/hardlink.c | 76 ++++ lib/fstree/src/mknode.c | 101 +++++ lib/fstree/src/post_process.c | 221 +++++++++ lib/io/Makemodule.am | 17 +- lib/io/get_line.c | 118 ----- lib/io/internal.h | 31 -- lib/io/istream.c | 86 ---- lib/io/ostream.c | 68 --- lib/io/printf.c | 30 -- lib/io/src/get_line.c | 118 +++++ lib/io/src/internal.h | 31 ++ lib/io/src/istream.c | 86 ++++ lib/io/src/ostream.c | 68 +++ lib/io/src/printf.c | 30 ++ lib/io/src/unix/istream.c | 123 +++++ lib/io/src/unix/ostream.c | 173 ++++++++ lib/io/src/win32/istream.c | 138 ++++++ lib/io/src/win32/ostream.c | 199 +++++++++ lib/io/src/xfrm/istream.c | 106 +++++ lib/io/src/xfrm/ostream.c | 144 ++++++ lib/io/unix/istream.c | 123 ----- lib/io/unix/ostream.c | 173 -------- lib/io/win32/istream.c | 138 ------ lib/io/win32/ostream.c | 199 --------- lib/io/xfrm/istream.c | 106 ----- lib/io/xfrm/ostream.c | 144 ------ lib/sqfs/Makemodule.am | 81 ++-- lib/sqfs/block_processor/backend.c | 335 -------------- lib/sqfs/block_processor/block_processor.c | 358 --------------- lib/sqfs/block_processor/frontend.c | 243 ---------- lib/sqfs/block_processor/internal.h | 115 ----- lib/sqfs/block_writer.c | 238 ---------- lib/sqfs/comp/compressor.c | 218 --------- lib/sqfs/comp/gzip.c | 307 ------------- lib/sqfs/comp/internal.h | 46 -- lib/sqfs/comp/lz4.c | 172 ------- lib/sqfs/comp/lzma.c | 281 ------------ lib/sqfs/comp/xz.c | 324 -------------- lib/sqfs/comp/zstd.c | 172 ------- lib/sqfs/data_reader.c | 374 ---------------- lib/sqfs/dir_reader/dir_reader.c | 366 --------------- lib/sqfs/dir_reader/get_path.c | 81 ---- lib/sqfs/dir_reader/internal.h | 52 --- lib/sqfs/dir_reader/read_tree.c | 288 ------------ lib/sqfs/dir_writer.c | 460 ------------------- lib/sqfs/frag_table.c | 204 --------- lib/sqfs/id_table.c | 162 ------- lib/sqfs/inode.c | 380 ---------------- lib/sqfs/meta_reader.c | 191 -------- lib/sqfs/meta_writer.c | 215 --------- lib/sqfs/misc.c | 17 - lib/sqfs/read_inode.c | 424 ------------------ lib/sqfs/read_super.c | 83 ---- lib/sqfs/read_table.c | 91 ---- lib/sqfs/readdir.c | 161 ------- lib/sqfs/src/block_processor/backend.c | 335 ++++++++++++++ lib/sqfs/src/block_processor/block_processor.c | 358 +++++++++++++++ lib/sqfs/src/block_processor/frontend.c | 243 ++++++++++ lib/sqfs/src/block_processor/internal.h | 115 +++++ lib/sqfs/src/block_writer.c | 238 ++++++++++ lib/sqfs/src/comp/compressor.c | 218 +++++++++ lib/sqfs/src/comp/gzip.c | 307 +++++++++++++ lib/sqfs/src/comp/internal.h | 46 ++ lib/sqfs/src/comp/lz4.c | 172 +++++++ lib/sqfs/src/comp/lzma.c | 281 ++++++++++++ lib/sqfs/src/comp/xz.c | 324 ++++++++++++++ lib/sqfs/src/comp/zstd.c | 172 +++++++ lib/sqfs/src/data_reader.c | 374 ++++++++++++++++ lib/sqfs/src/dir_reader/dir_reader.c | 366 +++++++++++++++ lib/sqfs/src/dir_reader/get_path.c | 81 ++++ lib/sqfs/src/dir_reader/internal.h | 52 +++ lib/sqfs/src/dir_reader/read_tree.c | 288 ++++++++++++ lib/sqfs/src/dir_writer.c | 460 +++++++++++++++++++ lib/sqfs/src/frag_table.c | 204 +++++++++ lib/sqfs/src/id_table.c | 162 +++++++ lib/sqfs/src/inode.c | 380 ++++++++++++++++ lib/sqfs/src/meta_reader.c | 191 ++++++++ lib/sqfs/src/meta_writer.c | 215 +++++++++ lib/sqfs/src/misc.c | 17 + lib/sqfs/src/read_inode.c | 424 ++++++++++++++++++ lib/sqfs/src/read_super.c | 83 ++++ lib/sqfs/src/read_table.c | 91 ++++ lib/sqfs/src/readdir.c | 161 +++++++ lib/sqfs/src/super.c | 50 +++ lib/sqfs/src/unix/io_file.c | 196 ++++++++ lib/sqfs/src/win32/io_file.c | 237 ++++++++++ lib/sqfs/src/write_inode.c | 220 +++++++++ lib/sqfs/src/write_super.c | 39 ++ lib/sqfs/src/write_table.c | 81 ++++ lib/sqfs/src/xattr/xattr.c | 49 ++ lib/sqfs/src/xattr/xattr_reader.c | 336 ++++++++++++++ lib/sqfs/src/xattr/xattr_writer.c | 127 ++++++ lib/sqfs/src/xattr/xattr_writer.h | 63 +++ lib/sqfs/src/xattr/xattr_writer_flush.c | 347 +++++++++++++++ lib/sqfs/src/xattr/xattr_writer_record.c | 145 ++++++ lib/sqfs/super.c | 50 --- lib/sqfs/unix/io_file.c | 196 -------- lib/sqfs/win32/io_file.c | 237 ---------- lib/sqfs/write_inode.c | 220 --------- lib/sqfs/write_super.c | 39 -- lib/sqfs/write_table.c | 81 ---- lib/sqfs/xattr/xattr.c | 49 -- lib/sqfs/xattr/xattr_reader.c | 336 -------------- lib/sqfs/xattr/xattr_writer.c | 127 ------ lib/sqfs/xattr/xattr_writer.h | 63 --- lib/sqfs/xattr/xattr_writer_flush.c | 347 --------------- lib/sqfs/xattr/xattr_writer_record.c | 145 ------ lib/tar/Makemodule.am | 15 +- lib/tar/checksum.c | 48 -- lib/tar/cleanup.c | 42 -- lib/tar/internal.h | 46 -- lib/tar/number.c | 79 ---- lib/tar/padd_file.c | 19 - lib/tar/pax_header.c | 402 ----------------- lib/tar/read_header.c | 304 ------------- lib/tar/read_sparse_map_new.c | 115 ----- lib/tar/read_sparse_map_old.c | 99 ----- lib/tar/record_to_memory.c | 41 -- lib/tar/src/checksum.c | 48 ++ lib/tar/src/cleanup.c | 42 ++ lib/tar/src/internal.h | 46 ++ lib/tar/src/number.c | 79 ++++ lib/tar/src/padd_file.c | 19 + lib/tar/src/pax_header.c | 402 +++++++++++++++++ lib/tar/src/read_header.c | 304 +++++++++++++ lib/tar/src/read_sparse_map_new.c | 115 +++++ lib/tar/src/read_sparse_map_old.c | 99 +++++ lib/tar/src/record_to_memory.c | 41 ++ lib/tar/src/write_header.c | 282 ++++++++++++ lib/tar/write_header.c | 282 ------------ lib/util/Makemodule.am | 35 +- lib/util/alloc.c | 37 -- lib/util/array.c | 115 ----- lib/util/base64_decode.c | 103 ----- lib/util/canonicalize_name.c | 60 --- lib/util/fast_urem_by_const.h | 77 ---- lib/util/file_cmp.c | 41 -- lib/util/filename_sane.c | 78 ---- lib/util/hash_table.c | 417 ----------------- lib/util/hex_decode.c | 34 -- lib/util/is_memory_zero.c | 54 --- lib/util/mempool.c | 216 --------- lib/util/mkdir_p.c | 170 ------- lib/util/rbtree.c | 267 ----------- lib/util/source_date_epoch.c | 44 -- lib/util/src/alloc.c | 37 ++ lib/util/src/array.c | 115 +++++ lib/util/src/base64_decode.c | 103 +++++ lib/util/src/canonicalize_name.c | 60 +++ lib/util/src/fast_urem_by_const.h | 77 ++++ lib/util/src/file_cmp.c | 41 ++ lib/util/src/filename_sane.c | 78 ++++ lib/util/src/hash_table.c | 417 +++++++++++++++++ lib/util/src/hex_decode.c | 34 ++ lib/util/src/is_memory_zero.c | 54 +++ lib/util/src/mempool.c | 216 +++++++++ lib/util/src/mkdir_p.c | 170 +++++++ lib/util/src/rbtree.c | 267 +++++++++++ lib/util/src/source_date_epoch.c | 44 ++ lib/util/src/str_table.c | 183 ++++++++ lib/util/src/threadpool.c | 392 ++++++++++++++++ lib/util/src/threadpool_serial.c | 162 +++++++ lib/util/src/xxhash.c | 112 +++++ lib/util/str_table.c | 183 -------- lib/util/threadpool.c | 392 ---------------- lib/util/threadpool_serial.c | 162 ------- lib/util/xxhash.c | 112 ----- lib/xfrm/Makemodule.am | 12 +- lib/xfrm/bzip2.c | 151 ------- lib/xfrm/compress.c | 102 ----- lib/xfrm/gzip.c | 144 ------ lib/xfrm/src/bzip2.c | 151 +++++++ lib/xfrm/src/compress.c | 102 +++++ lib/xfrm/src/gzip.c | 144 ++++++ lib/xfrm/src/xz.c | 195 ++++++++ lib/xfrm/src/zstd.c | 136 ++++++ lib/xfrm/xz.c | 195 -------- lib/xfrm/zstd.c | 136 ------ tests/gensquashfs/Makemodule.am | 48 +- tests/libutil/Makemodule.am | 4 +- 325 files changed, 24926 insertions(+), 24949 deletions(-) delete mode 100644 bin/gensquashfs/dirscan_xattr.c delete mode 100644 bin/gensquashfs/filemap_xattr.c delete mode 100644 bin/gensquashfs/fstree_from_dir.c delete mode 100644 bin/gensquashfs/fstree_from_file.c delete mode 100644 bin/gensquashfs/mkfs.c delete mode 100644 bin/gensquashfs/mkfs.h delete mode 100644 bin/gensquashfs/options.c delete mode 100644 bin/gensquashfs/selinux.c delete mode 100644 bin/gensquashfs/sort_by_file.c create mode 100644 bin/gensquashfs/src/dirscan_xattr.c create mode 100644 bin/gensquashfs/src/filemap_xattr.c create mode 100644 bin/gensquashfs/src/fstree_from_dir.c create mode 100644 bin/gensquashfs/src/fstree_from_file.c create mode 100644 bin/gensquashfs/src/mkfs.c create mode 100644 bin/gensquashfs/src/mkfs.h create mode 100644 bin/gensquashfs/src/options.c create mode 100644 bin/gensquashfs/src/selinux.c create mode 100644 bin/gensquashfs/src/sort_by_file.c delete mode 100644 bin/rdsquashfs/describe.c delete mode 100644 bin/rdsquashfs/dump_xattrs.c delete mode 100644 bin/rdsquashfs/fill_files.c delete mode 100644 bin/rdsquashfs/list_files.c delete mode 100644 bin/rdsquashfs/options.c delete mode 100644 bin/rdsquashfs/rdsquashfs.c delete mode 100644 bin/rdsquashfs/rdsquashfs.h delete mode 100644 bin/rdsquashfs/restore_fstree.c create mode 100644 bin/rdsquashfs/src/describe.c create mode 100644 bin/rdsquashfs/src/dump_xattrs.c create mode 100644 bin/rdsquashfs/src/fill_files.c create mode 100644 bin/rdsquashfs/src/list_files.c create mode 100644 bin/rdsquashfs/src/options.c create mode 100644 bin/rdsquashfs/src/rdsquashfs.c create mode 100644 bin/rdsquashfs/src/rdsquashfs.h create mode 100644 bin/rdsquashfs/src/restore_fstree.c create mode 100644 bin/rdsquashfs/src/stat.c delete mode 100644 bin/rdsquashfs/stat.c delete mode 100644 bin/sqfs2tar/options.c delete mode 100644 bin/sqfs2tar/sqfs2tar.c delete mode 100644 bin/sqfs2tar/sqfs2tar.h create mode 100644 bin/sqfs2tar/src/options.c create mode 100644 bin/sqfs2tar/src/sqfs2tar.c create mode 100644 bin/sqfs2tar/src/sqfs2tar.h create mode 100644 bin/sqfs2tar/src/write_tree.c create mode 100644 bin/sqfs2tar/src/xattr.c delete mode 100644 bin/sqfs2tar/write_tree.c delete mode 100644 bin/sqfs2tar/xattr.c delete mode 100644 bin/sqfsdiff/compare_dir.c delete mode 100644 bin/sqfsdiff/compare_files.c delete mode 100644 bin/sqfsdiff/extract.c delete mode 100644 bin/sqfsdiff/node_compare.c delete mode 100644 bin/sqfsdiff/options.c delete mode 100644 bin/sqfsdiff/sqfsdiff.c delete mode 100644 bin/sqfsdiff/sqfsdiff.h create mode 100644 bin/sqfsdiff/src/compare_dir.c create mode 100644 bin/sqfsdiff/src/compare_files.c create mode 100644 bin/sqfsdiff/src/extract.c create mode 100644 bin/sqfsdiff/src/node_compare.c create mode 100644 bin/sqfsdiff/src/options.c create mode 100644 bin/sqfsdiff/src/sqfsdiff.c create mode 100644 bin/sqfsdiff/src/sqfsdiff.h create mode 100644 bin/sqfsdiff/src/super.c create mode 100644 bin/sqfsdiff/src/util.c delete mode 100644 bin/sqfsdiff/super.c delete mode 100644 bin/sqfsdiff/util.c delete mode 100644 bin/tar2sqfs/options.c delete mode 100644 bin/tar2sqfs/process_tarball.c create mode 100644 bin/tar2sqfs/src/options.c create mode 100644 bin/tar2sqfs/src/process_tarball.c create mode 100644 bin/tar2sqfs/src/tar2sqfs.c create mode 100644 bin/tar2sqfs/src/tar2sqfs.h delete mode 100644 bin/tar2sqfs/tar2sqfs.c delete mode 100644 bin/tar2sqfs/tar2sqfs.h delete mode 100644 lib/common/comp_lzo.c delete mode 100644 lib/common/comp_opt.c delete mode 100644 lib/common/compress.c delete mode 100644 lib/common/data_reader_dump.c delete mode 100644 lib/common/data_writer.c delete mode 100644 lib/common/data_writer_ostream.c delete mode 100644 lib/common/hardlink.c delete mode 100644 lib/common/parse_size.c delete mode 100644 lib/common/perror.c delete mode 100644 lib/common/print_size.c delete mode 100644 lib/common/print_version.c create mode 100644 lib/common/src/comp_lzo.c create mode 100644 lib/common/src/comp_opt.c create mode 100644 lib/common/src/compress.c create mode 100644 lib/common/src/data_reader_dump.c create mode 100644 lib/common/src/data_writer.c create mode 100644 lib/common/src/data_writer_ostream.c create mode 100644 lib/common/src/hardlink.c create mode 100644 lib/common/src/parse_size.c create mode 100644 lib/common/src/perror.c create mode 100644 lib/common/src/print_size.c create mode 100644 lib/common/src/print_version.c create mode 100644 lib/common/src/writer/cleanup.c create mode 100644 lib/common/src/writer/finish.c create mode 100644 lib/common/src/writer/init.c create mode 100644 lib/common/src/writer/serialize_fstree.c delete mode 100644 lib/common/writer/cleanup.c delete mode 100644 lib/common/writer/finish.c delete mode 100644 lib/common/writer/init.c delete mode 100644 lib/common/writer/serialize_fstree.c delete mode 100644 lib/compat/chdir.c delete mode 100644 lib/compat/fnmatch.c delete mode 100644 lib/compat/getopt.c delete mode 100644 lib/compat/getopt_long.c delete mode 100644 lib/compat/getsubopt.c delete mode 100644 lib/compat/mockups.c delete mode 100644 lib/compat/path_to_windows.c create mode 100644 lib/compat/src/chdir.c create mode 100644 lib/compat/src/fnmatch.c create mode 100644 lib/compat/src/getopt.c create mode 100644 lib/compat/src/getopt_long.c create mode 100644 lib/compat/src/getsubopt.c create mode 100644 lib/compat/src/mockups.c create mode 100644 lib/compat/src/path_to_windows.c create mode 100644 lib/compat/src/strchrnul.c create mode 100644 lib/compat/src/strndup.c create mode 100644 lib/compat/src/w32_perror.c create mode 100644 lib/compat/src/w32_stdio.c create mode 100644 lib/compat/src/w32_wmain.c delete mode 100644 lib/compat/strchrnul.c delete mode 100644 lib/compat/strndup.c delete mode 100644 lib/compat/w32_perror.c delete mode 100644 lib/compat/w32_stdio.c delete mode 100644 lib/compat/w32_wmain.c delete mode 100644 lib/fstree/add_by_path.c delete mode 100644 lib/fstree/fstree.c delete mode 100644 lib/fstree/get_by_path.c delete mode 100644 lib/fstree/get_path.c delete mode 100644 lib/fstree/hardlink.c delete mode 100644 lib/fstree/mknode.c delete mode 100644 lib/fstree/post_process.c create mode 100644 lib/fstree/src/add_by_path.c create mode 100644 lib/fstree/src/fstree.c create mode 100644 lib/fstree/src/get_by_path.c create mode 100644 lib/fstree/src/get_path.c create mode 100644 lib/fstree/src/hardlink.c create mode 100644 lib/fstree/src/mknode.c create mode 100644 lib/fstree/src/post_process.c delete mode 100644 lib/io/get_line.c delete mode 100644 lib/io/internal.h delete mode 100644 lib/io/istream.c delete mode 100644 lib/io/ostream.c delete mode 100644 lib/io/printf.c create mode 100644 lib/io/src/get_line.c create mode 100644 lib/io/src/internal.h create mode 100644 lib/io/src/istream.c create mode 100644 lib/io/src/ostream.c create mode 100644 lib/io/src/printf.c create mode 100644 lib/io/src/unix/istream.c create mode 100644 lib/io/src/unix/ostream.c create mode 100644 lib/io/src/win32/istream.c create mode 100644 lib/io/src/win32/ostream.c create mode 100644 lib/io/src/xfrm/istream.c create mode 100644 lib/io/src/xfrm/ostream.c delete mode 100644 lib/io/unix/istream.c delete mode 100644 lib/io/unix/ostream.c delete mode 100644 lib/io/win32/istream.c delete mode 100644 lib/io/win32/ostream.c delete mode 100644 lib/io/xfrm/istream.c delete mode 100644 lib/io/xfrm/ostream.c delete mode 100644 lib/sqfs/block_processor/backend.c delete mode 100644 lib/sqfs/block_processor/block_processor.c delete mode 100644 lib/sqfs/block_processor/frontend.c delete mode 100644 lib/sqfs/block_processor/internal.h delete mode 100644 lib/sqfs/block_writer.c delete mode 100644 lib/sqfs/comp/compressor.c delete mode 100644 lib/sqfs/comp/gzip.c delete mode 100644 lib/sqfs/comp/internal.h delete mode 100644 lib/sqfs/comp/lz4.c delete mode 100644 lib/sqfs/comp/lzma.c delete mode 100644 lib/sqfs/comp/xz.c delete mode 100644 lib/sqfs/comp/zstd.c delete mode 100644 lib/sqfs/data_reader.c delete mode 100644 lib/sqfs/dir_reader/dir_reader.c delete mode 100644 lib/sqfs/dir_reader/get_path.c delete mode 100644 lib/sqfs/dir_reader/internal.h delete mode 100644 lib/sqfs/dir_reader/read_tree.c delete mode 100644 lib/sqfs/dir_writer.c delete mode 100644 lib/sqfs/frag_table.c delete mode 100644 lib/sqfs/id_table.c delete mode 100644 lib/sqfs/inode.c delete mode 100644 lib/sqfs/meta_reader.c delete mode 100644 lib/sqfs/meta_writer.c delete mode 100644 lib/sqfs/misc.c delete mode 100644 lib/sqfs/read_inode.c delete mode 100644 lib/sqfs/read_super.c delete mode 100644 lib/sqfs/read_table.c delete mode 100644 lib/sqfs/readdir.c create mode 100644 lib/sqfs/src/block_processor/backend.c create mode 100644 lib/sqfs/src/block_processor/block_processor.c create mode 100644 lib/sqfs/src/block_processor/frontend.c create mode 100644 lib/sqfs/src/block_processor/internal.h create mode 100644 lib/sqfs/src/block_writer.c create mode 100644 lib/sqfs/src/comp/compressor.c create mode 100644 lib/sqfs/src/comp/gzip.c create mode 100644 lib/sqfs/src/comp/internal.h create mode 100644 lib/sqfs/src/comp/lz4.c create mode 100644 lib/sqfs/src/comp/lzma.c create mode 100644 lib/sqfs/src/comp/xz.c create mode 100644 lib/sqfs/src/comp/zstd.c create mode 100644 lib/sqfs/src/data_reader.c create mode 100644 lib/sqfs/src/dir_reader/dir_reader.c create mode 100644 lib/sqfs/src/dir_reader/get_path.c create mode 100644 lib/sqfs/src/dir_reader/internal.h create mode 100644 lib/sqfs/src/dir_reader/read_tree.c create mode 100644 lib/sqfs/src/dir_writer.c create mode 100644 lib/sqfs/src/frag_table.c create mode 100644 lib/sqfs/src/id_table.c create mode 100644 lib/sqfs/src/inode.c create mode 100644 lib/sqfs/src/meta_reader.c create mode 100644 lib/sqfs/src/meta_writer.c create mode 100644 lib/sqfs/src/misc.c create mode 100644 lib/sqfs/src/read_inode.c create mode 100644 lib/sqfs/src/read_super.c create mode 100644 lib/sqfs/src/read_table.c create mode 100644 lib/sqfs/src/readdir.c create mode 100644 lib/sqfs/src/super.c create mode 100644 lib/sqfs/src/unix/io_file.c create mode 100644 lib/sqfs/src/win32/io_file.c create mode 100644 lib/sqfs/src/write_inode.c create mode 100644 lib/sqfs/src/write_super.c create mode 100644 lib/sqfs/src/write_table.c create mode 100644 lib/sqfs/src/xattr/xattr.c create mode 100644 lib/sqfs/src/xattr/xattr_reader.c create mode 100644 lib/sqfs/src/xattr/xattr_writer.c create mode 100644 lib/sqfs/src/xattr/xattr_writer.h create mode 100644 lib/sqfs/src/xattr/xattr_writer_flush.c create mode 100644 lib/sqfs/src/xattr/xattr_writer_record.c delete mode 100644 lib/sqfs/super.c delete mode 100644 lib/sqfs/unix/io_file.c delete mode 100644 lib/sqfs/win32/io_file.c delete mode 100644 lib/sqfs/write_inode.c delete mode 100644 lib/sqfs/write_super.c delete mode 100644 lib/sqfs/write_table.c delete mode 100644 lib/sqfs/xattr/xattr.c delete mode 100644 lib/sqfs/xattr/xattr_reader.c delete mode 100644 lib/sqfs/xattr/xattr_writer.c delete mode 100644 lib/sqfs/xattr/xattr_writer.h delete mode 100644 lib/sqfs/xattr/xattr_writer_flush.c delete mode 100644 lib/sqfs/xattr/xattr_writer_record.c delete mode 100644 lib/tar/checksum.c delete mode 100644 lib/tar/cleanup.c delete mode 100644 lib/tar/internal.h delete mode 100644 lib/tar/number.c delete mode 100644 lib/tar/padd_file.c delete mode 100644 lib/tar/pax_header.c delete mode 100644 lib/tar/read_header.c delete mode 100644 lib/tar/read_sparse_map_new.c delete mode 100644 lib/tar/read_sparse_map_old.c delete mode 100644 lib/tar/record_to_memory.c create mode 100644 lib/tar/src/checksum.c create mode 100644 lib/tar/src/cleanup.c create mode 100644 lib/tar/src/internal.h create mode 100644 lib/tar/src/number.c create mode 100644 lib/tar/src/padd_file.c create mode 100644 lib/tar/src/pax_header.c create mode 100644 lib/tar/src/read_header.c create mode 100644 lib/tar/src/read_sparse_map_new.c create mode 100644 lib/tar/src/read_sparse_map_old.c create mode 100644 lib/tar/src/record_to_memory.c create mode 100644 lib/tar/src/write_header.c delete mode 100644 lib/tar/write_header.c delete mode 100644 lib/util/alloc.c delete mode 100644 lib/util/array.c delete mode 100644 lib/util/base64_decode.c delete mode 100644 lib/util/canonicalize_name.c delete mode 100644 lib/util/fast_urem_by_const.h delete mode 100644 lib/util/file_cmp.c delete mode 100644 lib/util/filename_sane.c delete mode 100644 lib/util/hash_table.c delete mode 100644 lib/util/hex_decode.c delete mode 100644 lib/util/is_memory_zero.c delete mode 100644 lib/util/mempool.c delete mode 100644 lib/util/mkdir_p.c delete mode 100644 lib/util/rbtree.c delete mode 100644 lib/util/source_date_epoch.c create mode 100644 lib/util/src/alloc.c create mode 100644 lib/util/src/array.c create mode 100644 lib/util/src/base64_decode.c create mode 100644 lib/util/src/canonicalize_name.c create mode 100644 lib/util/src/fast_urem_by_const.h create mode 100644 lib/util/src/file_cmp.c create mode 100644 lib/util/src/filename_sane.c create mode 100644 lib/util/src/hash_table.c create mode 100644 lib/util/src/hex_decode.c create mode 100644 lib/util/src/is_memory_zero.c create mode 100644 lib/util/src/mempool.c create mode 100644 lib/util/src/mkdir_p.c create mode 100644 lib/util/src/rbtree.c create mode 100644 lib/util/src/source_date_epoch.c create mode 100644 lib/util/src/str_table.c create mode 100644 lib/util/src/threadpool.c create mode 100644 lib/util/src/threadpool_serial.c create mode 100644 lib/util/src/xxhash.c delete mode 100644 lib/util/str_table.c delete mode 100644 lib/util/threadpool.c delete mode 100644 lib/util/threadpool_serial.c delete mode 100644 lib/util/xxhash.c delete mode 100644 lib/xfrm/bzip2.c delete mode 100644 lib/xfrm/compress.c delete mode 100644 lib/xfrm/gzip.c create mode 100644 lib/xfrm/src/bzip2.c create mode 100644 lib/xfrm/src/compress.c create mode 100644 lib/xfrm/src/gzip.c create mode 100644 lib/xfrm/src/xz.c create mode 100644 lib/xfrm/src/zstd.c delete mode 100644 lib/xfrm/xz.c delete mode 100644 lib/xfrm/zstd.c diff --git a/bin/gensquashfs/Makemodule.am b/bin/gensquashfs/Makemodule.am index c6a98a2..7edc39a 100644 --- a/bin/gensquashfs/Makemodule.am +++ b/bin/gensquashfs/Makemodule.am @@ -1,10 +1,9 @@ -gensquashfs_SOURCES = bin/gensquashfs/mkfs.c bin/gensquashfs/mkfs.h -gensquashfs_SOURCES += bin/gensquashfs/options.c bin/gensquashfs/selinux.c -gensquashfs_SOURCES += bin/gensquashfs/dirscan_xattr.c -gensquashfs_SOURCES += bin/gensquashfs/filemap_xattr.c -gensquashfs_SOURCES += bin/gensquashfs/fstree_from_file.c -gensquashfs_SOURCES += bin/gensquashfs/fstree_from_dir.c -gensquashfs_SOURCES += bin/gensquashfs/sort_by_file.c +gensquashfs_SOURCES = bin/gensquashfs/src/mkfs.c bin/gensquashfs/src/mkfs.h \ + bin/gensquashfs/src/options.c bin/gensquashfs/src/selinux.c \ + bin/gensquashfs/src/dirscan_xattr.c bin/gensquashfs/src/filemap_xattr.c\ + bin/gensquashfs/src/fstree_from_file.c \ + bin/gensquashfs/src/fstree_from_dir.c \ + bin/gensquashfs/src/sort_by_file.c gensquashfs_LDADD = libcommon.a libsquashfs.la libfstree.a libio.a gensquashfs_LDADD += libutil.a libcompat.a $(LZO_LIBS) $(PTHREAD_LIBS) gensquashfs_CPPFLAGS = $(AM_CPPFLAGS) diff --git a/bin/gensquashfs/dirscan_xattr.c b/bin/gensquashfs/dirscan_xattr.c deleted file mode 100644 index 7d4e552..0000000 --- a/bin/gensquashfs/dirscan_xattr.c +++ /dev/null @@ -1,220 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * dirscan_xattr.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "mkfs.h" - -#ifdef HAVE_SYS_XATTR_H -static char *get_full_path(const char *prefix, tree_node_t *node) -{ - char *path = NULL, *new = NULL; - size_t path_len, prefix_len; - int ret; - - path = fstree_get_path(node); - if (path == NULL) - goto fail; - - ret = canonicalize_name(path); - assert(ret == 0); - - path_len = strlen(path); - prefix_len = strlen(prefix); - - while (prefix_len > 0 && prefix[prefix_len - 1] == '/') - --prefix_len; - - if (prefix_len > 0) { - new = realloc(path, path_len + prefix_len + 2); - if (new == NULL) - goto fail; - - path = new; - - memmove(path + prefix_len + 1, path, path_len + 1); - memcpy(path, prefix, prefix_len); - path[prefix_len] = '/'; - } - - return path; -fail: - perror("getting full path for xattr scan"); - free(path); - return NULL; -} - -static int xattr_from_path(sqfs_xattr_writer_t *xwr, const char *path) -{ - char *key, *value = NULL, *buffer = NULL; - ssize_t buflen, vallen, keylen; - int ret; - - buflen = llistxattr(path, NULL, 0); - if (buflen < 0) { - fprintf(stderr, "llistxattr %s: %s", path, strerror(errno)); - return -1; - } - - if (buflen == 0) - return 0; - - buffer = malloc(buflen); - if (buffer == NULL) { - perror("xattr name buffer"); - return -1; - } - - buflen = llistxattr(path, buffer, buflen); - if (buflen == -1) { - fprintf(stderr, "llistxattr %s: %s", path, strerror(errno)); - goto fail; - } - - key = buffer; - while (buflen > 0) { - vallen = lgetxattr(path, key, NULL, 0); - if (vallen == -1) { - fprintf(stderr, "lgetxattr %s: %s", - path, strerror(errno)); - goto fail; - } - - if (vallen > 0) { - value = calloc(1, vallen); - if (value == NULL) { - perror("allocating xattr value buffer"); - goto fail; - } - - vallen = lgetxattr(path, key, value, vallen); - if (vallen == -1) { - fprintf(stderr, "lgetxattr %s: %s\n", - path, strerror(errno)); - goto fail; - } - - ret = sqfs_xattr_writer_add(xwr, key, value, vallen); - if (ret) { - sqfs_perror(path, - "storing xattr key-value pairs", - ret); - goto fail; - } - - free(value); - value = NULL; - } - - keylen = strlen(key) + 1; - buflen -= keylen; - key += keylen; - } - - free(buffer); - return 0; -fail: - free(value); - free(buffer); - return -1; -} -#endif - -static int xattr_xcan_dfs(const char *path_prefix, void *selinux_handle, - sqfs_xattr_writer_t *xwr, bool scan_xattr, void *xattr_map, - tree_node_t *node) -{ - char *path = NULL; - int ret; - - ret = sqfs_xattr_writer_begin(xwr, 0); - if (ret) { - sqfs_perror(node->name, "recoding xattr key-value pairs\n", - ret); - return -1; - } - -#ifdef HAVE_SYS_XATTR_H - if (scan_xattr) { - path = get_full_path(path_prefix, node); - if (path == NULL) - return -1; - - ret = xattr_from_path(xwr, path); - free(path); - path = NULL; - if (ret) { - ret = -1; - goto out; - } - } -#else - (void)path_prefix; -#endif - - if (selinux_handle != NULL || xattr_map != NULL) { - path = fstree_get_path(node); - - if (path == NULL) { - perror("reconstructing absolute path"); - ret = -1; - goto out; - } - } - - if (xattr_map != NULL) { - ret = xattr_apply_map_file(path, xattr_map, xwr); - - if (ret) { - ret = -1; - goto out; - } - } - - if (selinux_handle != NULL) { - ret = selinux_relable_node(selinux_handle, xwr, node, path); - - if (ret) { - ret = -1; - goto out; - } - } - - if (sqfs_xattr_writer_end(xwr, &node->xattr_idx)) { - sqfs_perror(node->name, "completing xattr key-value pairs", - ret); - ret = -1; - goto out; - } - - if (S_ISDIR(node->mode)) { - node = node->data.dir.children; - - while (node != NULL) { - if (xattr_xcan_dfs(path_prefix, selinux_handle, xwr, - scan_xattr, xattr_map, node)) { - ret = -1; - goto out; - } - - node = node->next; - } - } - -out: - free(path); - return ret; -} - -int xattrs_from_dir(fstree_t *fs, const char *path, void *selinux_handle, - void *xattr_map, sqfs_xattr_writer_t *xwr, bool scan_xattr) -{ - if (xwr == NULL) - return 0; - - if (selinux_handle == NULL && !scan_xattr && xattr_map == NULL) - return 0; - - return xattr_xcan_dfs(path, selinux_handle, xwr, scan_xattr, xattr_map, fs->root); -} diff --git a/bin/gensquashfs/filemap_xattr.c b/bin/gensquashfs/filemap_xattr.c deleted file mode 100644 index dd76b50..0000000 --- a/bin/gensquashfs/filemap_xattr.c +++ /dev/null @@ -1,254 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * filemap_xattr.c - * - * Copyright (C) 2022 Enno Boland - */ -#include "fstree.h" -#include "mkfs.h" -#include - -#define NEW_FILE_START "# file: " - -static void print_error(const char *filename, size_t line_num, const char *err) -{ - fprintf(stderr, "%s: " PRI_SZ ": %s\n", filename, line_num, err); -} - -// Taken from attr-2.5.1/tools/setfattr.c -static sqfs_u8 *decode(const char *filename, size_t line_num, - const char *value, size_t *size) -{ - sqfs_u8 *decoded = NULL; - - if (*size == 0) { - decoded = (sqfs_u8 *)strdup(""); - if (decoded == NULL) - goto fail_alloc; - return decoded; - } - - if (value[0] == '0' && (value[1] == 'x' || value[1] == 'X')) { - *size = ((*size) - 2) / 2; - - decoded = calloc(1, (*size) + 1); - if (decoded == NULL) - goto fail_alloc; - - if (hex_decode(value + 2, (*size) * 2, decoded, *size)) - goto fail_encode; - } else if (value[0] == '0' && (value[1] == 's' || value[1] == 'S')) { - size_t input_len = *size - 2; - - *size = (input_len / 4) * 3; - - decoded = calloc(1, (*size) + 1); - if (decoded == NULL) - goto fail_alloc; - - if (base64_decode(value + 2, input_len, decoded, size)) - goto fail_encode; - } else { - const char *v = value, *end = value + *size; - sqfs_u8 *d; - - if (end > v + 1 && *v == '"' && *(end - 1) == '"') { - v++; - end--; - } - - decoded = calloc(1, (*size) + 1); - if (decoded == NULL) - goto fail_alloc; - - d = decoded; - - while (v < end) { - if (v[0] == '\\') { - if (v[1] == '\\' || v[1] == '"') { - *d++ = *++v; - v++; - } else if (v[1] >= '0' && v[1] <= '7') { - int c = 0; - v++; - c = (*v++ - '0'); - if (*v >= '0' && *v <= '7') - c = (c << 3) + (*v++ - '0'); - if (*v >= '0' && *v <= '7') - c = (c << 3) + (*v++ - '0'); - *d++ = c; - } else - *d++ = *v++; - } else - *d++ = *v++; - } - *size = d - decoded; - } - return decoded; -fail_alloc: - fprintf(stderr, "out of memory\n"); - return NULL; -fail_encode: - print_error(filename, line_num, "bad input encoding"); - free(decoded); - return NULL; -} - -static int parse_file_name(const char *filename, size_t line_num, - char *line, struct XattrMap *map) -{ - struct XattrMapPattern *current_file; - char *file_name = strdup(line + strlen(NEW_FILE_START)); - - if (file_name == NULL) - goto fail_alloc; - - current_file = calloc(1, sizeof(struct XattrMapPattern)); - if (current_file == NULL) - goto fail_alloc; - - current_file->next = map->patterns; - map->patterns = current_file; - - if (canonicalize_name(file_name)) { - print_error(filename, line_num, "invalid absolute path"); - free(current_file); - free(file_name); - return -1; - } - - current_file->path = file_name; - return 0; -fail_alloc: - fprintf(stderr, "out of memory\n"); - free(file_name); - return -1; -} - -static int parse_xattr(const char *filename, size_t line_num, char *key_start, - char *value_start, struct XattrMap *map) -{ - size_t len; - struct XattrMapPattern *current_pattern = map->patterns; - struct XattrMapEntry *current_entry; - - if (current_pattern == NULL) { - print_error(filename, line_num, "no file specified yet"); - return -1; - } - - current_entry = calloc(1, sizeof(struct XattrMapEntry)); - if (current_entry == NULL) { - return -1; - } - current_entry->next = current_pattern->entries; - current_pattern->entries = current_entry; - - current_entry->key = strdup(key_start); - len = strlen(value_start); - current_entry->value = decode(filename, line_num, value_start, &len); - current_entry->value_len = len; - - return 0; -} - -void * -xattr_open_map_file(const char *path) { - struct XattrMap *map; - size_t line_num = 1; - char *p = NULL; - istream_t *file = istream_open_file(path); - if (file == NULL) { - return NULL; - } - - map = calloc(1, sizeof(struct XattrMap)); - if (map == NULL) - goto fail_close; - - for (;;) { - char *line = NULL; - int ret = istream_get_line(file, &line, &line_num, - ISTREAM_LINE_LTRIM | - ISTREAM_LINE_RTRIM | - ISTREAM_LINE_SKIP_EMPTY); - if (ret < 0) - goto fail; - if (ret > 0) - break; - - if (strncmp(NEW_FILE_START, line, strlen(NEW_FILE_START)) == 0) { - ret = parse_file_name(path, line_num, line, map); - } else if ((p = strchr(line, '='))) { - *(p++) = '\0'; - ret = parse_xattr(path, line_num, line, p, map); - } else if (line[0] != '#') { - print_error(path, line_num, "not a key-value pair"); - ret = -1; - } - - ++line_num; - free(line); - if (ret < 0) - goto fail; - } - - sqfs_drop(file); - return map; -fail: - xattr_close_map_file(map); -fail_close: - sqfs_drop(file); - return NULL; -} - -void -xattr_close_map_file(void *xattr_map) { - struct XattrMap *map = xattr_map; - while (map->patterns != NULL) { - struct XattrMapPattern *file = map->patterns; - map->patterns = file->next; - while (file->entries != NULL) { - struct XattrMapEntry *entry = file->entries; - file->entries = entry->next; - free(entry->key); - free(entry->value); - free(entry); - } - free(file->path); - free(file); - } - free(xattr_map); -} - -int -xattr_apply_map_file(char *path, void *map, sqfs_xattr_writer_t *xwr) { - struct XattrMap *xattr_map = map; - int ret = 0; - const struct XattrMapPattern *pat; - const struct XattrMapEntry *entry; - - for (pat = xattr_map->patterns; pat != NULL; pat = pat->next) { - char *patstr = pat->path; - const char *stripped = path; - - if (patstr[0] != '/' && stripped[0] == '/') { - stripped++; - } - - if (strcmp(patstr, stripped) == 0) { - printf("Applying xattrs for %s", path); - for (entry = pat->entries; entry != NULL; entry = entry->next) { - printf(" %s = \n", entry->key); - fwrite(entry->value, entry->value_len, 1, stdout); - puts("\n"); - ret = sqfs_xattr_writer_add( - xwr, entry->key, entry->value, entry->value_len); - if (ret < 0) { - return ret; - } - } - } - } - return ret; -} diff --git a/bin/gensquashfs/fstree_from_dir.c b/bin/gensquashfs/fstree_from_dir.c deleted file mode 100644 index 5b3f003..0000000 --- a/bin/gensquashfs/fstree_from_dir.c +++ /dev/null @@ -1,493 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * fstree_from_dir.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "config.h" -#include "mkfs.h" - -#include -#include -#include -#include - -#if defined(_WIN32) || defined(__WINDOWS__) -#define UNIX_EPOCH_ON_W32 11644473600UL -#define W32_TICS_PER_SEC 10000000UL - -static sqfs_u32 w32time_to_sqfs_time(const FILETIME *ft) -{ - sqfs_u64 w32ts; - - w32ts = ft->dwHighDateTime; - w32ts <<= 32UL; - w32ts |= ft->dwLowDateTime; - - w32ts /= W32_TICS_PER_SEC; - - if (w32ts <= UNIX_EPOCH_ON_W32) - return 0; - - w32ts -= UNIX_EPOCH_ON_W32; - - return (w32ts < 0x0FFFFFFFFUL) ? w32ts : 0xFFFFFFFF; -} - -static int add_node(fstree_t *fs, tree_node_t *root, - scan_node_callback cb, void *user, - unsigned int flags, - const LPWIN32_FIND_DATAW entry) -{ - tree_node_t *n; - DWORD length; - - if (entry->cFileName[0] == '.') { - if (entry->cFileName[1] == '\0') - return 0; - - if (entry->cFileName[1] == '.' && entry->cFileName[2] == '\0') - return 0; - } - - length = WideCharToMultiByte(CP_UTF8, 0, entry->cFileName, - -1, NULL, 0, NULL, NULL); - if (length <= 0) { - w32_perror("converting path to UTF-8"); - return -1; - } - - n = calloc(1, sizeof(*n) + length + 1); - if (n == NULL) { - fprintf(stderr, "creating tree node: out-of-memory\n"); - return -1; - } - - n->name = (char *)n->payload; - WideCharToMultiByte(CP_UTF8, 0, entry->cFileName, -1, - n->name, length + 1, NULL, NULL); - - if (entry->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { - if (flags & DIR_SCAN_NO_DIR) { - free(n); - return 0; - } - - n->mode = S_IFDIR | 0755; - } else { - if (flags & DIR_SCAN_NO_FILE) { - free(n); - return 0; - } - - n->mode = S_IFREG | 0644; - } - - if (cb != NULL) { - int ret = cb(user, fs, n); - - if (ret != 0) { - free(n); - return ret < 0 ? ret : 0; - } - } - - if (flags & DIR_SCAN_KEEP_TIME) { - n->mod_time = w32time_to_sqfs_time(&(entry->ftLastWriteTime)); - } else { - n->mod_time = fs->defaults.st_mtime; - } - - fstree_insert_sorted(root, n); - return 0; -} - -static int scan_dir(fstree_t *fs, tree_node_t *root, - const char *path, const WCHAR *wpath, - scan_node_callback cb, void *user, - unsigned int flags) -{ - WIN32_FIND_DATAW entry; - HANDLE dirhnd; - - dirhnd = FindFirstFileW(wpath, &entry); - - if (dirhnd == INVALID_HANDLE_VALUE) - goto fail_perror; - - do { - if (add_node(fs, root, cb, user, flags, &entry)) - goto fail; - } while (FindNextFileW(dirhnd, &entry)); - - if (GetLastError() != ERROR_NO_MORE_FILES) - goto fail_perror; - - FindClose(dirhnd); - return 0; -fail_perror: - w32_perror(path); -fail: - if (dirhnd != INVALID_HANDLE_VALUE) - FindClose(dirhnd); - return -1; -} - -int fstree_from_dir(fstree_t *fs, tree_node_t *root, - const char *path, scan_node_callback cb, - void *user, unsigned int flags) -{ - WCHAR *wpath = NULL, *new = NULL; - size_t len, newlen; - tree_node_t *n; - - /* path -> to_wchar(path) + L"\*" */ - wpath = path_to_windows(path); - if (wpath == NULL) { - fprintf(stderr, "%s: allocation failure.\n", path); - return -1; - } - - for (len = 0; wpath[len] != '\0'; ++len) - ; - - newlen = len + 1; - - if (len > 0 && wpath[len - 1] != '\\') - newlen += 1; - - new = realloc(wpath, sizeof(wpath[0]) * (newlen + 1)); - if (new == NULL) { - fprintf(stderr, "%s: allocation failure.\n", path); - goto fail; - } - - wpath = new; - - if (len > 0 && wpath[len - 1] != '\\') - wpath[len++] = '\\'; - - wpath[len++] = '*'; - wpath[len++] = '\0'; - - /* scan directory contents */ - if (scan_dir(fs, root, path, wpath, cb, user, flags)) - goto fail; - - free(wpath); - wpath = NULL; - - /* recursion step */ - if (flags & DIR_SCAN_NO_RECURSION) - return 0; - - for (n = root->data.dir.children; n != NULL; n = n->next) { - if (!S_ISDIR(n->mode)) - continue; - - if (fstree_from_subdir(fs, n, path, n->name, cb, user, flags)) - return -1; - } - - return 0; -fail: - free(wpath); - return -1; -} - -int fstree_from_subdir(fstree_t *fs, tree_node_t *root, - const char *path, const char *subdir, - scan_node_callback cb, void *user, - unsigned int flags) -{ - size_t len, plen, slen; - WCHAR *wpath = NULL; - char *temp = NULL; - tree_node_t *n; - - plen = strlen(path); - slen = subdir == NULL ? 0 : strlen(subdir); - - if (slen == 0) - return fstree_from_dir(fs, root, path, cb, user, flags); - - len = plen + 1 + slen + 2; - - temp = calloc(1, len + 1); - if (temp == NULL) { - fprintf(stderr, "%s/%s: allocation failure.\n", path, subdir); - return -1; - } - - memcpy(temp, path, plen); - temp[plen] = '/'; - memcpy(temp + plen + 1, subdir, slen); - temp[plen + 1 + slen ] = '/'; - temp[plen + 1 + slen + 1] = '*'; - temp[plen + 1 + slen + 2] = '\0'; - - wpath = path_to_windows(temp); - if (wpath == NULL) { - fprintf(stderr, "%s: allocation failure.\n", temp); - goto fail; - } - - if (scan_dir(fs, root, temp, wpath, cb, user, flags)) - goto fail; - - free(wpath); - wpath = NULL; - - if (flags & DIR_SCAN_NO_RECURSION) { - free(temp); - return 0; - } - - temp[plen + 1 + slen] = '\0'; - - for (n = root->data.dir.children; n != NULL; n = n->next) { - if (!S_ISDIR(n->mode)) - continue; - - if (fstree_from_subdir(fs, n, temp, n->name, cb, user, flags)) - goto fail; - } - - free(temp); - return 0; -fail: - free(temp); - free(wpath); - return -1; - -} -#else -static void discard_node(tree_node_t *root, tree_node_t *n) -{ - tree_node_t *it; - - if (n == root->data.dir.children) { - root->data.dir.children = n->next; - } else { - it = root->data.dir.children; - - while (it != NULL && it->next != n) - it = it->next; - - if (it != NULL) - it->next = n->next; - } - - free(n); -} - -static int populate_dir(int dir_fd, fstree_t *fs, tree_node_t *root, - dev_t devstart, scan_node_callback cb, - void *user, unsigned int flags) -{ - char *extra = NULL; - struct dirent *ent; - int ret, childfd; - struct stat sb; - tree_node_t *n; - DIR *dir; - - dir = fdopendir(dir_fd); - if (dir == NULL) { - perror("fdopendir"); - close(dir_fd); - return -1; - } - - /* XXX: fdopendir can dup and close dir_fd internally - and still be compliant with the spec. */ - dir_fd = dirfd(dir); - - for (;;) { - errno = 0; - ent = readdir(dir); - - if (ent == NULL) { - if (errno) { - perror("readdir"); - goto fail; - } - break; - } - - if (!strcmp(ent->d_name, "..") || !strcmp(ent->d_name, ".")) - continue; - - if (fstatat(dir_fd, ent->d_name, &sb, AT_SYMLINK_NOFOLLOW)) { - perror(ent->d_name); - goto fail; - } - - switch (sb.st_mode & S_IFMT) { - case S_IFSOCK: - if (flags & DIR_SCAN_NO_SOCK) - continue; - break; - case S_IFLNK: - if (flags & DIR_SCAN_NO_SLINK) - continue; - break; - case S_IFREG: - if (flags & DIR_SCAN_NO_FILE) - continue; - break; - case S_IFBLK: - if (flags & DIR_SCAN_NO_BLK) - continue; - break; - case S_IFCHR: - if (flags & DIR_SCAN_NO_CHR) - continue; - break; - case S_IFIFO: - if (flags & DIR_SCAN_NO_FIFO) - continue; - break; - default: - break; - } - - if ((flags & DIR_SCAN_ONE_FILESYSTEM) && sb.st_dev != devstart) - continue; - - if (S_ISLNK(sb.st_mode)) { - size_t size; - - if ((sizeof(sb.st_size) > sizeof(size_t)) && - sb.st_size > SIZE_MAX) { - errno = EOVERFLOW; - goto fail_rdlink; - } - - if (SZ_ADD_OV((size_t)sb.st_size, 1, &size)) { - errno = EOVERFLOW; - goto fail_rdlink; - } - - extra = calloc(1, size); - if (extra == NULL) - goto fail_rdlink; - - if (readlinkat(dir_fd, ent->d_name, - extra, (size_t)sb.st_size) < 0) { - goto fail_rdlink; - } - - extra[sb.st_size] = '\0'; - } - - if (!(flags & DIR_SCAN_KEEP_TIME)) - sb.st_mtime = fs->defaults.st_mtime; - - if (S_ISDIR(sb.st_mode) && (flags & DIR_SCAN_NO_DIR)) { - n = fstree_get_node_by_path(fs, root, ent->d_name, - false, false); - if (n == NULL) - continue; - - ret = 0; - } else { - n = fstree_mknode(root, ent->d_name, - strlen(ent->d_name), extra, &sb); - if (n == NULL) { - perror("creating tree node"); - goto fail; - } - - ret = (cb == NULL) ? 0 : cb(user, fs, n); - } - - free(extra); - extra = NULL; - - if (ret < 0) - goto fail; - - if (ret > 0) { - discard_node(root, n); - continue; - } - - if (S_ISDIR(n->mode) && !(flags & DIR_SCAN_NO_RECURSION)) { - childfd = openat(dir_fd, n->name, O_DIRECTORY | - O_RDONLY | O_CLOEXEC); - if (childfd < 0) { - perror(n->name); - goto fail; - } - - if (populate_dir(childfd, fs, n, devstart, - cb, user, flags)) { - goto fail; - } - } - } - - closedir(dir); - return 0; -fail_rdlink: - perror("readlink"); -fail: - closedir(dir); - free(extra); - return -1; -} - -int fstree_from_subdir(fstree_t *fs, tree_node_t *root, - const char *path, const char *subdir, - scan_node_callback cb, void *user, - unsigned int flags) -{ - struct stat sb; - int fd, subfd; - - if (!S_ISDIR(root->mode)) { - fprintf(stderr, - "scanning %s/%s into %s: target is not a directory\n", - path, subdir == NULL ? "" : subdir, root->name); - return -1; - } - - fd = open(path, O_DIRECTORY | O_RDONLY | O_CLOEXEC); - if (fd < 0) { - perror(path); - return -1; - } - - if (subdir != NULL) { - subfd = openat(fd, subdir, O_DIRECTORY | O_RDONLY | O_CLOEXEC); - - if (subfd < 0) { - fprintf(stderr, "%s/%s: %s\n", path, subdir, - strerror(errno)); - close(fd); - return -1; - } - - close(fd); - fd = subfd; - } - - if (fstat(fd, &sb)) { - fprintf(stderr, "%s/%s: %s\n", path, - subdir == NULL ? "" : subdir, - strerror(errno)); - close(fd); - return -1; - } - - return populate_dir(fd, fs, root, sb.st_dev, cb, user, flags); -} - -int fstree_from_dir(fstree_t *fs, tree_node_t *root, - const char *path, scan_node_callback cb, - void *user, unsigned int flags) -{ - return fstree_from_subdir(fs, root, path, NULL, cb, user, flags); -} -#endif diff --git a/bin/gensquashfs/fstree_from_file.c b/bin/gensquashfs/fstree_from_file.c deleted file mode 100644 index e26d4b1..0000000 --- a/bin/gensquashfs/fstree_from_file.c +++ /dev/null @@ -1,591 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * fstree_from_file.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "config.h" - -#include "util/util.h" -#include "io/file.h" -#include "compat.h" -#include "mkfs.h" - -#include -#include -#include -#include -#include - -struct glob_context { - const char *filename; - size_t line_num; - - struct stat *basic; - unsigned int glob_flags; - - char *name_pattern; -}; - -enum { - GLOB_MODE_FROM_SRC = 0x01, - GLOB_UID_FROM_SRC = 0x02, - GLOB_GID_FROM_SRC = 0x04, - GLOB_FLAG_PATH = 0x08, -}; - -static const struct { - const char *name; - unsigned int clear_flag; - unsigned int set_flag; -} glob_scan_flags[] = { - { "-type b", DIR_SCAN_NO_BLK, 0 }, - { "-type c", DIR_SCAN_NO_CHR, 0 }, - { "-type d", DIR_SCAN_NO_DIR, 0 }, - { "-type p", DIR_SCAN_NO_FIFO, 0 }, - { "-type f", DIR_SCAN_NO_FILE, 0 }, - { "-type l", DIR_SCAN_NO_SLINK, 0 }, - { "-type s", DIR_SCAN_NO_SOCK, 0 }, - { "-xdev", 0, DIR_SCAN_ONE_FILESYSTEM }, - { "-mount", 0, DIR_SCAN_ONE_FILESYSTEM }, - { "-keeptime", 0, DIR_SCAN_KEEP_TIME }, - { "-nonrecursive", 0, DIR_SCAN_NO_RECURSION }, -}; - -static int add_generic(fstree_t *fs, const char *filename, size_t line_num, - const char *path, struct stat *sb, - const char *basepath, unsigned int glob_flags, - const char *extra) -{ - (void)basepath; - (void)glob_flags; - - if (fstree_add_generic(fs, path, sb, extra) == NULL) { - fprintf(stderr, "%s: " PRI_SZ ": %s: %s\n", - filename, line_num, path, strerror(errno)); - return -1; - } - - return 0; -} - -static int add_device(fstree_t *fs, const char *filename, size_t line_num, - const char *path, struct stat *sb, const char *basepath, - unsigned int glob_flags, const char *extra) -{ - unsigned int maj, min; - char c; - - if (sscanf(extra, "%c %u %u", &c, &maj, &min) != 3) { - fprintf(stderr, "%s: " PRI_SZ ": " - "expected ' major minor'\n", - filename, line_num); - return -1; - } - - if (c == 'c' || c == 'C') { - sb->st_mode |= S_IFCHR; - } else if (c == 'b' || c == 'B') { - sb->st_mode |= S_IFBLK; - } else { - fprintf(stderr, "%s: " PRI_SZ ": unknown device type '%c'\n", - filename, line_num, c); - return -1; - } - - sb->st_rdev = makedev(maj, min); - return add_generic(fs, filename, line_num, path, sb, basepath, - glob_flags, NULL); -} - -static int add_file(fstree_t *fs, const char *filename, size_t line_num, - const char *path, struct stat *basic, const char *basepath, - unsigned int glob_flags, const char *extra) -{ - if (extra == NULL || *extra == '\0') - extra = path; - - return add_generic(fs, filename, line_num, path, basic, - basepath, glob_flags, extra); -} - -static int add_hard_link(fstree_t *fs, const char *filename, size_t line_num, - const char *path, struct stat *basic, - const char *basepath, unsigned int glob_flags, - const char *extra) -{ - (void)basepath; - (void)glob_flags; - (void)basic; - - if (fstree_add_hard_link(fs, path, extra) == NULL) { - fprintf(stderr, "%s: " PRI_SZ ": %s\n", - filename, line_num, strerror(errno)); - return -1; - } - return 0; -} - -static int glob_node_callback(void *user, fstree_t *fs, tree_node_t *node) -{ - struct glob_context *ctx = user; - char *path; - int ret; - (void)fs; - - if (!(ctx->glob_flags & GLOB_MODE_FROM_SRC)) { - node->mode &= ~(07777); - node->mode |= ctx->basic->st_mode & 07777; - } - - if (!(ctx->glob_flags & GLOB_UID_FROM_SRC)) - node->uid = ctx->basic->st_uid; - - if (!(ctx->glob_flags & GLOB_GID_FROM_SRC)) - node->gid = ctx->basic->st_gid; - - if (ctx->name_pattern != NULL) { - if (ctx->glob_flags & GLOB_FLAG_PATH) { - path = fstree_get_path(node); - if (path == NULL) { - fprintf(stderr, "%s: " PRI_SZ ": %s\n", - ctx->filename, ctx->line_num, - strerror(errno)); - return -1; - } - - ret = canonicalize_name(path); - assert(ret == 0); - - ret = fnmatch(ctx->name_pattern, path, FNM_PATHNAME); - free(path); - } else { - ret = fnmatch(ctx->name_pattern, node->name, 0); - } - - if (ret != 0) - return 1; - } - - return 0; -} - -static size_t name_string_length(const char *str) -{ - size_t len = 0; - int start; - - if (*str == '"' || *str == '\'') { - start = *str; - ++len; - - while (str[len] != '\0' && str[len] != start) - ++len; - - if (str[len] == start) - ++len; - } else { - while (str[len] != '\0' && !isspace(str[len])) - ++len; - } - - return len; -} - -static void quote_remove(char *str) -{ - char *dst = str; - int start = *(str++); - - if (start != '\'' && start != '"') - return; - - while (*str != start && *str != '\0') - *(dst++) = *(str++); - - *(dst++) = '\0'; -} - -static int glob_files(fstree_t *fs, const char *filename, size_t line_num, - const char *path, struct stat *basic, - const char *basepath, unsigned int glob_flags, - const char *extra) -{ - unsigned int scan_flags = 0, all_flags; - struct glob_context ctx; - bool first_clear_flag; - size_t i, count, len; - tree_node_t *root; - int ret; - - memset(&ctx, 0, sizeof(ctx)); - ctx.filename = filename; - ctx.line_num = line_num; - ctx.basic = basic; - ctx.glob_flags = glob_flags; - - /* fetch the actual target node */ - root = fstree_get_node_by_path(fs, fs->root, path, true, false); - if (root == NULL) { - fprintf(stderr, "%s: " PRI_SZ ": %s: %s\n", - filename, line_num, path, strerror(errno)); - return -1; - } - - /* process options */ - first_clear_flag = true; - - all_flags = DIR_SCAN_NO_BLK | DIR_SCAN_NO_CHR | DIR_SCAN_NO_DIR | - DIR_SCAN_NO_FIFO | DIR_SCAN_NO_FILE | DIR_SCAN_NO_SLINK | - DIR_SCAN_NO_SOCK; - - while (extra != NULL && *extra != '\0') { - count = sizeof(glob_scan_flags) / sizeof(glob_scan_flags[0]); - - for (i = 0; i < count; ++i) { - len = strlen(glob_scan_flags[i].name); - if (strncmp(extra, glob_scan_flags[i].name, len) != 0) - continue; - - if (isspace(extra[len])) { - extra += len; - while (isspace(*extra)) - ++extra; - break; - } - } - - if (i < count) { - if (glob_scan_flags[i].clear_flag != 0 && - first_clear_flag) { - scan_flags |= all_flags; - first_clear_flag = false; - } - - scan_flags &= ~(glob_scan_flags[i].clear_flag); - scan_flags |= glob_scan_flags[i].set_flag; - continue; - } - - if (strncmp(extra, "-name", 5) == 0 && isspace(extra[5])) { - for (extra += 5; isspace(*extra); ++extra) - ; - - len = name_string_length(extra); - - free(ctx.name_pattern); - ctx.name_pattern = strndup(extra, len); - extra += len; - - while (isspace(*extra)) - ++extra; - - quote_remove(ctx.name_pattern); - continue; - } - - if (strncmp(extra, "-path", 5) == 0 && isspace(extra[5])) { - for (extra += 5; isspace(*extra); ++extra) - ; - - len = name_string_length(extra); - - free(ctx.name_pattern); - ctx.name_pattern = strndup(extra, len); - extra += len; - - while (isspace(*extra)) - ++extra; - - quote_remove(ctx.name_pattern); - ctx.glob_flags |= GLOB_FLAG_PATH; - continue; - } - - if (extra[0] == '-') { - if (extra[1] == '-' && isspace(extra[2])) { - extra += 2; - while (isspace(*extra)) - ++extra; - break; - } - - fprintf(stderr, "%s: " PRI_SZ ": unknown option.\n", - filename, line_num); - free(ctx.name_pattern); - return -1; - } else { - break; - } - } - - if (extra != NULL && *extra == '\0') - extra = NULL; - - /* do the scan */ - if (basepath == NULL) { - if (extra == NULL) { - ret = fstree_from_dir(fs, root, ".", glob_node_callback, - &ctx, scan_flags); - } else { - ret = fstree_from_dir(fs, root, extra, - glob_node_callback, - &ctx, scan_flags); - } - } else { - ret = fstree_from_subdir(fs, root, basepath, extra, - glob_node_callback, &ctx, - scan_flags); - } - - free(ctx.name_pattern); - return ret; -} - -static const struct callback_t { - const char *keyword; - unsigned int mode; - bool need_extra; - bool is_glob; - bool allow_root; - int (*callback)(fstree_t *fs, const char *filename, size_t line_num, - const char *path, struct stat *sb, - const char *basepath, unsigned int glob_flags, - const char *extra); -} file_list_hooks[] = { - { "dir", S_IFDIR, false, false, true, add_generic }, - { "slink", S_IFLNK, true, false, false, add_generic }, - { "link", 0, true, false, false, add_hard_link }, - { "nod", 0, true, false, false, add_device }, - { "pipe", S_IFIFO, false, false, false, add_generic }, - { "sock", S_IFSOCK, false, false, false, add_generic }, - { "file", S_IFREG, false, false, false, add_file }, - { "glob", 0, false, true, true, glob_files }, -}; - -#define NUM_HOOKS (sizeof(file_list_hooks) / sizeof(file_list_hooks[0])) - -static char *skip_space(char *str) -{ - if (!isspace(*str)) - return NULL; - while (isspace(*str)) - ++str; - return str; -} - -static char *read_u32(char *str, sqfs_u32 *out, sqfs_u32 base) -{ - *out = 0; - - if (!isdigit(*str)) - return NULL; - - while (isdigit(*str)) { - sqfs_u32 x = *(str++) - '0'; - - if (x >= base || (*out) > (0xFFFFFFFF - x) / base) - return NULL; - - (*out) = (*out) * base + x; - } - - return str; -} - -static char *read_str(char *str, char **out) -{ - *out = str; - - if (*str == '"') { - char *ptr = str++; - - while (*str != '\0' && *str != '"') { - if (str[0] == '\\' && - (str[1] == '"' || str[1] == '\\')) { - *(ptr++) = str[1]; - str += 2; - } else { - *(ptr++) = *(str++); - } - } - - if (str[0] != '"' || !isspace(str[1])) - return NULL; - - *ptr = '\0'; - ++str; - } else { - while (*str != '\0' && !isspace(*str)) - ++str; - - if (!isspace(*str)) - return NULL; - - *(str++) = '\0'; - } - - while (isspace(*str)) - ++str; - - return str; -} - -static int handle_line(fstree_t *fs, const char *filename, - size_t line_num, char *line, - const char *basepath) -{ - const char *extra = NULL, *msg = NULL; - const struct callback_t *cb = NULL; - unsigned int glob_flags = 0; - sqfs_u32 uid, gid, mode; - struct stat sb; - char *path; - - for (size_t i = 0; i < NUM_HOOKS; ++i) { - size_t len = strlen(file_list_hooks[i].keyword); - if (strncmp(file_list_hooks[i].keyword, line, len) != 0) - continue; - - if (isspace(line[len])) { - cb = file_list_hooks + i; - line = skip_space(line + len); - break; - } - } - - if (cb == NULL) - goto fail_kw; - - if ((line = read_str(line, &path)) == NULL) - goto fail_ent; - - if (canonicalize_name(path)) - goto fail_ent; - - if (*path == '\0' && !cb->allow_root) - goto fail_root; - - if (cb->is_glob && *line == '*') { - ++line; - mode = 0; - glob_flags |= GLOB_MODE_FROM_SRC; - } else { - if ((line = read_u32(line, &mode, 8)) == NULL || mode > 07777) - goto fail_mode; - } - - if ((line = skip_space(line)) == NULL) - goto fail_ent; - - if (cb->is_glob && *line == '*') { - ++line; - uid = 0; - glob_flags |= GLOB_UID_FROM_SRC; - } else { - if ((line = read_u32(line, &uid, 10)) == NULL) - goto fail_uid_gid; - } - - if ((line = skip_space(line)) == NULL) - goto fail_ent; - - if (cb->is_glob && *line == '*') { - ++line; - gid = 0; - glob_flags |= GLOB_GID_FROM_SRC; - } else { - if ((line = read_u32(line, &gid, 10)) == NULL) - goto fail_uid_gid; - } - - if ((line = skip_space(line)) != NULL && *line != '\0') - extra = line; - - if (cb->need_extra && extra == NULL) - goto fail_no_extra; - - /* forward to callback */ - memset(&sb, 0, sizeof(sb)); - sb.st_mtime = fs->defaults.st_mtime; - sb.st_mode = mode | cb->mode; - sb.st_uid = uid; - sb.st_gid = gid; - - return cb->callback(fs, filename, line_num, path, - &sb, basepath, glob_flags, extra); -fail_root: - fprintf(stderr, "%s: " PRI_SZ ": cannot use / as argument for %s.\n", - filename, line_num, cb->keyword); - return -1; -fail_no_extra: - fprintf(stderr, "%s: " PRI_SZ ": missing argument for %s.\n", - filename, line_num, cb->keyword); - return -1; -fail_uid_gid: - msg = "uid & gid must be decimal numbers < 2^32"; - goto out_desc; -fail_mode: - msg = "mode must be an octal number <= 07777"; - goto out_desc; -fail_kw: - msg = "unknown entry type"; - goto out_desc; -fail_ent: - msg = "error in entry description"; - goto out_desc; -out_desc: - fprintf(stderr, "%s: " PRI_SZ ": %s.\n", filename, line_num, msg); - fputs("expected: []\n", - stderr); - return -1; -} - -int fstree_from_file_stream(fstree_t *fs, istream_t *fp, const char *basepath) -{ - const char *filename; - size_t line_num = 1; - char *line; - int ret; - - filename = istream_get_filename(fp); - - for (;;) { - ret = istream_get_line(fp, &line, &line_num, - ISTREAM_LINE_LTRIM | ISTREAM_LINE_SKIP_EMPTY); - if (ret < 0) - return -1; - if (ret > 0) - break; - - if (line[0] != '#') { - if (handle_line(fs, filename, line_num, - line, basepath)) { - goto fail_line; - } - } - - free(line); - ++line_num; - } - - return 0; -fail_line: - free(line); - return -1; -} - -int fstree_from_file(fstree_t *fs, const char *filename, const char *basepath) -{ - istream_t *fp; - int ret; - - fp = istream_open_file(filename); - if (fp == NULL) - return -1; - - ret = fstree_from_file_stream(fs, fp, basepath); - - sqfs_drop(fp); - return ret; -} diff --git a/bin/gensquashfs/mkfs.c b/bin/gensquashfs/mkfs.c deleted file mode 100644 index c773dd7..0000000 --- a/bin/gensquashfs/mkfs.c +++ /dev/null @@ -1,215 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * mkfs.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "mkfs.h" - -static int pack_files(sqfs_block_processor_t *data, fstree_t *fs, - options_t *opt) -{ - sqfs_u64 filesize; - sqfs_file_t *file; - tree_node_t *node; - const char *path; - char *node_path; - file_info_t *fi; - int flags; - int ret; - - if (opt->packdir != NULL && chdir(opt->packdir) != 0) { - perror(opt->packdir); - return -1; - } - - for (fi = fs->files; fi != NULL; fi = fi->next) { - if (fi->input_file == NULL) { - node = container_of(fi, tree_node_t, data.file); - - node_path = fstree_get_path(node); - if (node_path == NULL) { - perror("reconstructing file path"); - return -1; - } - - ret = canonicalize_name(node_path); - assert(ret == 0); - - path = node_path; - } else { - node_path = NULL; - path = fi->input_file; - } - - if (!opt->cfg.quiet) - printf("packing %s\n", path); - - file = sqfs_open_file(path, SQFS_FILE_OPEN_READ_ONLY); - if (file == NULL) { - perror(path); - free(node_path); - return -1; - } - - flags = fi->flags; - filesize = file->get_size(file); - - if (opt->no_tail_packing && filesize > opt->cfg.block_size) - flags |= SQFS_BLK_DONT_FRAGMENT; - - ret = write_data_from_file(path, data, &fi->inode, file, flags); - sqfs_drop(file); - free(node_path); - - if (ret) - return -1; - } - - return 0; -} - -static int relabel_tree_dfs(const char *filename, sqfs_xattr_writer_t *xwr, - tree_node_t *n, void *selinux_handle) -{ - char *path = fstree_get_path(n); - int ret; - - if (path == NULL) { - perror("getting absolute node path for SELinux relabeling"); - return -1; - } - - ret = sqfs_xattr_writer_begin(xwr, 0); - if (ret) { - sqfs_perror(filename, "recording xattr key-value pairs", ret); - return -1; - } - - if (selinux_relable_node(selinux_handle, xwr, n, path)) { - free(path); - return -1; - } - - ret = sqfs_xattr_writer_end(xwr, &n->xattr_idx); - if (ret) { - sqfs_perror(filename, "flushing completed key-value pairs", - ret); - return -1; - } - - free(path); - - if (S_ISDIR(n->mode)) { - for (n = n->data.dir.children; n != NULL; n = n->next) { - if (relabel_tree_dfs(filename, xwr, n, selinux_handle)) - return -1; - } - } - - return 0; -} - -static int read_fstree(fstree_t *fs, options_t *opt, sqfs_xattr_writer_t *xwr, - void *selinux_handle) -{ - int ret; - - ret = fstree_from_file(fs, opt->infile, opt->packdir); - - if (ret == 0 && selinux_handle != NULL) - ret = relabel_tree_dfs(opt->cfg.filename, xwr, - fs->root, selinux_handle); - - return ret; -} - -static void override_owner_dfs(const options_t *opt, tree_node_t *n) -{ - if (opt->force_uid) - n->uid = opt->force_uid_value; - - if (opt->force_gid) - n->gid = opt->force_gid_value; - - if (S_ISDIR(n->mode)) { - for (n = n->data.dir.children; n != NULL; n = n->next) - override_owner_dfs(opt, n); - } -} - -int main(int argc, char **argv) -{ - int status = EXIT_FAILURE; - istream_t *sortfile = NULL; - void *sehnd = NULL; - void *xattrmap = NULL; - sqfs_writer_t sqfs; - options_t opt; - - process_command_line(&opt, argc, argv); - - if (sqfs_writer_init(&sqfs, &opt.cfg)) - return EXIT_FAILURE; - - if (opt.selinux != NULL) { - sehnd = selinux_open_context_file(opt.selinux); - if (sehnd == NULL) - goto out; - } - if (opt.xattr_file != NULL) { - xattrmap = xattr_open_map_file(opt.xattr_file); - if (xattrmap == NULL) - goto out; - } - - if (opt.sortfile != NULL) { - sortfile = istream_open_file(opt.sortfile); - if (sortfile == NULL) - goto out; - } - - if (opt.infile == NULL) { - if (fstree_from_dir(&sqfs.fs, sqfs.fs.root, opt.packdir, - NULL, NULL, opt.dirscan_flags)) { - goto out; - } - } else { - if (read_fstree(&sqfs.fs, &opt, sqfs.xwr, sehnd)) - goto out; - } - - if (opt.force_uid || opt.force_gid) - override_owner_dfs(&opt, sqfs.fs.root); - - if (fstree_post_process(&sqfs.fs)) - goto out; - - if (opt.infile == NULL) { - if (xattrs_from_dir(&sqfs.fs, opt.packdir, sehnd, xattrmap, - sqfs.xwr, opt.scan_xattr)) { - goto out; - } - } - - if (sortfile != NULL) { - if (fstree_sort_files(&sqfs.fs, sortfile)) - goto out; - } - - if (pack_files(sqfs.data, &sqfs.fs, &opt)) - goto out; - - if (sqfs_writer_finish(&sqfs, &opt.cfg)) - goto out; - - status = EXIT_SUCCESS; -out: - sqfs_writer_cleanup(&sqfs, status); - if (sehnd != NULL) - selinux_close_context_file(sehnd); - if (sortfile != NULL) - sqfs_drop(sortfile); - free(opt.packdir); - return status; -} diff --git a/bin/gensquashfs/mkfs.h b/bin/gensquashfs/mkfs.h deleted file mode 100644 index 53fb018..0000000 --- a/bin/gensquashfs/mkfs.h +++ /dev/null @@ -1,137 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * mkfs.h - * - * Copyright (C) 2019 David Oberhollenzer - * Copyright (C) 2022 Enno Boland - */ -#ifndef MKFS_H -#define MKFS_H - -#include "config.h" - -#include "common.h" -#include "fstree.h" -#include "util/util.h" -#include "io/file.h" - -#ifdef HAVE_SYS_XATTR_H -#include - -#if defined(__APPLE__) && defined(__MACH__) -#define llistxattr(path, list, size) \ - listxattr(path, list, size, XATTR_NOFOLLOW) - -#define lgetxattr(path, name, value, size) \ - getxattr(path, name, value, size, 0, XATTR_NOFOLLOW) -#endif -#endif - -#ifdef WITH_SELINUX -#include -#include -#endif - -#include -#include -#include -#include -#include -#include -#include -#include - -typedef struct { - sqfs_writer_cfg_t cfg; - unsigned int dirscan_flags; - const char *infile; - const char *selinux; - const char *xattr_file; - const char *sortfile; - bool no_tail_packing; - - /* copied from command line or constructed from infile argument - if not specified. Must be free'd. */ - char *packdir; - - unsigned int force_uid_value; - unsigned int force_gid_value; - bool force_uid; - bool force_gid; - - bool scan_xattr; -} options_t; - -struct XattrMapEntry { - char *key; - sqfs_u8 *value; - size_t value_len; - struct XattrMapEntry *next; -}; - -struct XattrMapPattern { - char *path; - struct XattrMapEntry *entries; - struct XattrMapPattern *next; -}; - -struct XattrMap { - struct XattrMapPattern *patterns; -}; - -void process_command_line(options_t *opt, int argc, char **argv); - -int xattrs_from_dir(fstree_t *fs, const char *path, void *selinux_handle, - void *xattr_map, sqfs_xattr_writer_t *xwr, bool scan_xattr); - -void *xattr_open_map_file(const char *path); - -int -xattr_apply_map_file(char *path, void *map, sqfs_xattr_writer_t *xwr); - -void xattr_close_map_file(void *xattr_map); - -void *selinux_open_context_file(const char *filename); - -int selinux_relable_node(void *sehnd, sqfs_xattr_writer_t *xwr, - tree_node_t *node, const char *path); - -void selinux_close_context_file(void *sehnd); - -/* - Parses the file format accepted by gensquashfs and produce a file system - tree from it. File input paths are interpreted as relative to the current - working directory. - - On failure, an error report with filename and line number is written - to stderr. - - Returns 0 on success. - */ -int fstree_from_file(fstree_t *fs, const char *filename, - const char *basepath); - -int fstree_from_file_stream(fstree_t *fs, istream_t *file, - const char *basepath); - -/* - Recursively scan a directory to build a file system tree. - - Returns 0 on success, prints to stderr on failure. - */ -int fstree_from_dir(fstree_t *fs, tree_node_t *root, - const char *path, scan_node_callback cb, void *user, - unsigned int flags); - -/* - Same as fstree_from_dir, but scans a sub-directory inside the specified path. - - Returns 0 on success, prints to stderr on failure. - */ -int fstree_from_subdir(fstree_t *fs, tree_node_t *root, - const char *path, const char *subdir, - scan_node_callback cb, void *user, unsigned int flags); - -int fstree_sort_files(fstree_t *fs, istream_t *sortfile); - -#endif /* MKFS_H */ diff --git a/bin/gensquashfs/options.c b/bin/gensquashfs/options.c deleted file mode 100644 index f263bce..0000000 --- a/bin/gensquashfs/options.c +++ /dev/null @@ -1,383 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * options.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "mkfs.h" - -enum { - ALL_ROOT_OPTION = 1, -}; - -static struct option long_opts[] = { - { "all-root", no_argument, NULL, ALL_ROOT_OPTION }, - { "set-uid", required_argument, NULL, 'u' }, - { "set-gid", required_argument, NULL, 'g' }, - { "compressor", required_argument, NULL, 'c' }, - { "block-size", required_argument, NULL, 'b' }, - { "dev-block-size", required_argument, NULL, 'B' }, - { "defaults", required_argument, NULL, 'd' }, - { "comp-extra", required_argument, NULL, 'X' }, - { "pack-file", required_argument, NULL, 'F' }, - { "pack-dir", required_argument, NULL, 'D' }, - { "num-jobs", required_argument, NULL, 'j' }, - { "queue-backlog", required_argument, NULL, 'Q' }, - { "keep-time", no_argument, NULL, 'k' }, -#ifdef HAVE_SYS_XATTR_H - { "keep-xattr", no_argument, NULL, 'x' }, -#endif - { "one-file-system", no_argument, NULL, 'o' }, - { "exportable", no_argument, NULL, 'e' }, - { "no-tail-packing", no_argument, NULL, 'T' }, - { "force", no_argument, NULL, 'f' }, - { "quiet", no_argument, NULL, 'q' }, -#ifdef WITH_SELINUX - { "selinux", required_argument, NULL, 's' }, -#endif - { "xattr-file", required_argument, NULL, 'A' }, - { "sort-file", required_argument, NULL, 'S' }, - { "version", no_argument, NULL, 'V' }, - { "help", no_argument, NULL, 'h' }, - { NULL, 0, NULL, 0 }, -}; - -static const char *short_opts = "F:D:X:c:b:B:d:u:g:j:Q:S:A:kxoefqThV" -#ifdef WITH_SELINUX -"s:" -#endif -#ifdef HAVE_SYS_XATTR_H -"x" -#endif -; - -static const char *help_string = -"Usage: gensquashfs [OPTIONS...] \n" -"\n" -"Possible options:\n" -"\n"; - -static const char *pack_options = -" --pack-file, -F Use a `gen_init_cpio` style description file.\n" -" The file format is specified below.\n" -" If --pack-dir is used, input file paths are\n" -" relative to the pack directory, otherwise\n" -" they are relative to the directory the pack\n" -" file is in.\n" -" --pack-dir, -D If --pack-file is used, this is the root path\n" -" relative to which to read files. If no pack\n" -" file is specified, pack the contents of the\n" -" given directory. The directory becomes the\n" -" file system root.\n" -"\n" -" --compressor, -c Select the compressor to use.\n" -" A list of available compressors is below.\n" -" --comp-extra, -X A comma separated list of extra options for\n" -" the selected compressor. Specify 'help' to\n" -" get a list of available options.\n" -" --num-jobs, -j Number of compressor jobs to create.\n" -" --queue-backlog, -Q Maximum number of data blocks in the thread\n" -" worker queue before the packer starts waiting\n" -" for the block processors to catch up.\n" -" Defaults to 10 times the number of jobs.\n" -" --block-size, -b Block size to use for Squashfs image.\n" -" Defaults to %u.\n" -" --dev-block-size, -B Device block size to padd the image to.\n" -" Defaults to %u.\n" -" --defaults, -d A comma separated list of default values for\n" -" implicitly created directories.\n" -"\n" -" Possible options:\n" -" uid= 0 if not set.\n" -" gid= 0 if not set.\n" -" mode= 0755 if not set.\n" -" mtime= 0 if not set.\n" -"\n" -" --set-uid, -u Force the owners user ID for ALL inodes to\n" -" this value, no matter what the pack file or\n" -" directory entries actually specify.\n" -" --set-gid, -g Force the owners group ID for ALL inodes to\n" -" this value, no matter what the pack file or\n" -" directory entries actually specify.\n" -" --all-root A short hand for `--set-uid 0 --set-gid 0`.\n" -"\n"; - -const char *extra_options = -" --sort-file, -S Specify a \"sort file\" that can be used to\n" -" micro manage the order of files during packing\n" -" and behaviour (compression, fragmentation, ..)\n" -"\n" -#ifdef WITH_SELINUX -" --selinux, -s Specify an SELinux label file to get context\n" -" attributes from.\n" -#endif -" --xattr-file, -A Specify an Xattr file to get extended attributes\n" -" for loading xattrs\n" -" --keep-time, -k When using --pack-dir only, use the timestamps\n" -" from the input files instead of setting\n" -" defaults on all input paths.\n" -" --keep-xattr, -x When using --pack-dir only, read and pack the\n" -" extended attributes from the input files.\n" -" --one-file-system, -o When using --pack-dir only, stay in local file\n" -" system and do not cross mount points.\n" -" --exportable, -e Generate an export table for NFS support.\n" -" --no-tail-packing, -T Do not perform tail end packing on files that\n" -" are larger than block size.\n" -" --force, -f Overwrite the output file if it exists.\n" -" --quiet, -q Do not print out progress reports.\n" -" --help, -h Print help text and exit.\n" -" --version, -V Print version information and exit.\n" -"\n"; - -const char *pack_details = -"Example of a pack file:\n" -"\n" -" # A simple squashfs image\n" -" dir /dev 0755 0 0\n" -" nod /dev/console 0600 0 0 c 5 1\n" -" dir /root 0700 0 0\n" -" \n" -" # `slink` for symlink, `link` for hard links\n" -" slink /lib 0777 0 0 /usr/lib\n" -" link /init 0777 0 0 /sbin/init\n" -" \n" -" # Add a file. Input is relative to listing or pack dir.\n" -" file /sbin/init 0755 0 0 ../init/sbin/init\n" -" \n" -" # Read bin/bash, relative to listing or pack dir.\n" -" # Implicitly create /bin.\n" -" file /bin/bash 0755 0 0\n" -" \n" -" # file name with a space in it.\n" -" file \"/opt/my app/\\\"special\\\"/data\" 0600 0 0\n" -" \n" -" # collect the contents of ./lib and put it under /usr/lib\n" -" glob /usr/lib 0755 0 0 -type d ./lib\n" -" glob /usr/lib 0755 0 0 -type f -name \"*.so.*\" ./lib\n" -" glob /usr/lib 0777 0 0 -type l -name \"*.so.*\" ./lib\n" -"\n\n"; - -const char *sort_details = -"When using a sort file, the specified paths are within the SquashFS image.\n" -"Files with lower priority are packed first, default priority is 0.\n" -"The sorting is stable, files with the same priority do not change place\n" -"relative to each other.\n" -"\n" -"Example:\n" -" # Specify a packing order with file globbing\n" -" -8000 [glob] bin/*\n" -" -5000 [glob] lib/*\n" -"\n" -" # glob_no_path means * is allowed to match /\n" -" -1000 [glob_no_path] share/*\n" -"\n" -" # Our boot loader needs this\n" -" -100000 [dont_compress,dont_fragment,nosparse] boot/vmlinuz\n" -"\n" -" # For demonstration, a quoted filename and no flags\n" -" 1337 \"usr/share/my \\\"special\\\" file \"\n" -"\n\n"; - -static const char *xattr_details = -"The format of xattr files tries to immitate the format generated\n" -"by `getfattr --dump`.\n" -"\n" -"Example:\n" -" # file: dev/\n" -" security.selinux=\"system_u:object_r:device_t:s0\"\n" -" user.beverage_preference=0xCAFECAFEDECAFBAD\n" -"\n" -" # file: dev/rfkill\n" -" security.selinux=\"system_u:object_r:wireless_device_t:s0\"\n" -" system.posix_acl_access=0sSGVsbG8gdGhlcmUgOi0pCg==\n" -"\n\n"; - -void process_command_line(options_t *opt, int argc, char **argv) -{ - bool have_compressor; - int i, ret; - - memset(opt, 0, sizeof(*opt)); - sqfs_writer_cfg_init(&opt->cfg); - - for (;;) { - i = getopt_long(argc, argv, short_opts, long_opts, NULL); - if (i == -1) - break; - - switch (i) { - case ALL_ROOT_OPTION: - opt->force_uid_value = 0; - opt->force_gid_value = 0; - opt->force_uid = true; - opt->force_gid = true; - break; - case 'u': - opt->force_uid_value = strtol(optarg, NULL, 0); - opt->force_uid = true; - break; - case 'g': - opt->force_gid_value = strtol(optarg, NULL, 0); - opt->force_gid = true; - break; - case 'T': - opt->no_tail_packing = true; - break; - case 'c': - have_compressor = true; - ret = sqfs_compressor_id_from_name(optarg); - - if (ret < 0) { - have_compressor = false; -#ifdef WITH_LZO - if (opt->cfg.comp_id == SQFS_COMP_LZO) - have_compressor = true; -#endif - } - - if (!have_compressor) { - fprintf(stderr, "Unsupported compressor '%s'\n", - optarg); - exit(EXIT_FAILURE); - } - - opt->cfg.comp_id = ret; - break; - case 'b': - if (parse_size("Block size", &opt->cfg.block_size, - optarg, 0)) { - exit(EXIT_FAILURE); - } - break; - case 'j': - opt->cfg.num_jobs = strtol(optarg, NULL, 0); - break; - case 'Q': - opt->cfg.max_backlog = strtol(optarg, NULL, 0); - break; - case 'B': - if (parse_size("Device block size", - &opt->cfg.devblksize, optarg, 0)) { - exit(EXIT_FAILURE); - } - if (opt->cfg.devblksize < 1024) { - fputs("Device block size must be at " - "least 1024\n", stderr); - exit(EXIT_FAILURE); - } - break; - case 'd': - opt->cfg.fs_defaults = optarg; - break; - case 'k': - opt->dirscan_flags |= DIR_SCAN_KEEP_TIME; - break; -#ifdef HAVE_SYS_XATTR_H - case 'x': - opt->scan_xattr = true; - break; -#endif - case 'o': - opt->dirscan_flags |= DIR_SCAN_ONE_FILESYSTEM; - break; - case 'e': - opt->cfg.exportable = true; - break; - case 'f': - opt->cfg.outmode |= SQFS_FILE_OPEN_OVERWRITE; - break; - case 'q': - opt->cfg.quiet = true; - break; - case 'X': - opt->cfg.comp_extra = optarg; - break; - case 'F': - opt->infile = optarg; - break; - case 'D': - free(opt->packdir); - opt->packdir = strdup(optarg); - if (opt->packdir == NULL) { - perror(optarg); - exit(EXIT_FAILURE); - } - break; -#ifdef WITH_SELINUX - case 's': - opt->selinux = optarg; - break; -#endif - case 'A': - opt->xattr_file = optarg; - break; - case 'S': - opt->sortfile = optarg; - break; - case 'h': - fputs(help_string, stdout); - printf(pack_options, SQFS_DEFAULT_BLOCK_SIZE, - SQFS_DEVBLK_SIZE); - fputs(extra_options, stdout); - fputs(pack_details, stdout); - fputs(sort_details, stdout); - fputs(xattr_details, stdout); - compressor_print_available(); - exit(EXIT_SUCCESS); - case 'V': - print_version("gensquashfs"); - exit(EXIT_SUCCESS); - default: - goto fail_arg; - } - } - - if (opt->cfg.num_jobs < 1) - opt->cfg.num_jobs = 1; - - if (opt->cfg.max_backlog < 1) - opt->cfg.max_backlog = 10 * opt->cfg.num_jobs; - - if (opt->cfg.comp_extra != NULL && - strcmp(opt->cfg.comp_extra, "help") == 0) { - compressor_print_help(opt->cfg.comp_id); - exit(EXIT_SUCCESS); - } - - if (opt->infile == NULL && opt->packdir == NULL) { - fputs("No input file or directory specified.\n", stderr); - goto fail_arg; - } - - if (optind >= argc) { - fputs("No output file specified.\n", stderr); - goto fail_arg; - } - - opt->cfg.filename = argv[optind++]; - - if (optind < argc) { - fputs("Unknown extra arguments specified.\n", stderr); - goto fail_arg; - } - - /* construct packdir if not specified */ - if (opt->packdir == NULL && opt->infile != NULL) { - const char *split = strrchr(opt->infile, '/'); - - if (split != NULL) { - opt->packdir = strndup(opt->infile, - split - opt->infile); - - if (opt->packdir == NULL) { - perror("constructing input directory path"); - exit(EXIT_FAILURE); - } - } - } - return; -fail_arg: - fputs("Try `gensquashfs --help' for more information.\n", stderr); - free(opt->packdir); - exit(EXIT_FAILURE); -} diff --git a/bin/gensquashfs/selinux.c b/bin/gensquashfs/selinux.c deleted file mode 100644 index 678723b..0000000 --- a/bin/gensquashfs/selinux.c +++ /dev/null @@ -1,78 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * selinux.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "mkfs.h" - -#define XATTR_NAME_SELINUX "security.selinux" -#define XATTR_VALUE_SELINUX "system_u:object_r:unlabeled_t:s0" - -#ifdef WITH_SELINUX -int selinux_relable_node(void *sehnd, sqfs_xattr_writer_t *xwr, - tree_node_t *node, const char *path) -{ - char *context = NULL; - int ret; - - if (selabel_lookup(sehnd, &context, path, node->mode) < 0) { - context = strdup(XATTR_VALUE_SELINUX); - if (context == NULL) - goto fail; - } - - ret = sqfs_xattr_writer_add(xwr, XATTR_NAME_SELINUX, - context, strlen(context)); - free(context); - - if (ret) - sqfs_perror(node->name, "storing SELinux xattr", ret); - - return ret; -fail: - perror("relabeling files"); - return -1; -} - -void *selinux_open_context_file(const char *filename) -{ - struct selabel_handle *sehnd; - struct selinux_opt seopts[] = { - { SELABEL_OPT_PATH, filename }, - }; - - sehnd = selabel_open(SELABEL_CTX_FILE, seopts, 1); - if (sehnd == NULL) - perror(filename); - - return sehnd; -} - -void selinux_close_context_file(void *sehnd) -{ - selabel_close(sehnd); -} -#else -int selinux_relable_node(void *sehnd, sqfs_xattr_writer_t *xwr, - tree_node_t *node, const char *path) -{ - (void)sehnd; (void)xwr; (void)node; (void)path; - fputs("Built without SELinux support, cannot add SELinux labels\n", - stderr); - return -1; -} - -void *selinux_open_context_file(const char *filename) -{ - (void)filename; - fputs("Built without SELinux support, cannot open contexts file\n", - stderr); - return NULL; -} - -void selinux_close_context_file(void *sehnd) -{ - (void)sehnd; -} -#endif diff --git a/bin/gensquashfs/sort_by_file.c b/bin/gensquashfs/sort_by_file.c deleted file mode 100644 index a555718..0000000 --- a/bin/gensquashfs/sort_by_file.c +++ /dev/null @@ -1,368 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * sort_by_file.c - * - * Copyright (C) 2021 David Oberhollenzer - */ -#include "config.h" - -#include "util/util.h" -#include "fstree.h" -#include "mkfs.h" - -#include "sqfs/block.h" - -#include -#include -#include - -static int decode_priority(const char *filename, size_t line_no, - char *line, sqfs_s64 *priority) -{ - bool negative = false; - size_t i = 0; - - if (line[0] == '-') { - negative = true; - i = 1; - } - - if (!isdigit(line[i])) - goto fail_number; - - *priority = 0; - - for (; isdigit(line[i]); ++i) { - sqfs_s64 x = line[i] - '0'; - - if ((*priority) >= ((0x7FFFFFFFFFFFFFFFL - x) / 10L)) - goto fail_ov; - - (*priority) = (*priority) * 10 + x; - } - - if (!isspace(line[i])) - goto fail_filename; - - while (isspace(line[i])) - ++i; - - if (line[i] == '\0') - goto fail_filename; - - if (negative) - (*priority) = -(*priority); - - memmove(line, line + i, strlen(line + i) + 1); - return 0; -fail_number: - fprintf(stderr, "%s: " PRI_SZ ": Line must start with " - "numeric sort priority.\n", - filename, line_no); - return -1; -fail_ov: - fprintf(stderr, "%s: " PRI_SZ ": Numeric overflow in sort priority.\n", - filename, line_no); - return -1; -fail_filename: - fprintf(stderr, "%s: " PRI_SZ ": Expacted ` ` " - "after sort priority.\n", - filename, line_no); - return -1; -} - -static int decode_filename(const char *filename, size_t line_no, char *buffer) -{ - char *src, *dst; - - if (buffer[0] == '"') { - src = buffer + 1; - dst = buffer; - - for (;;) { - if (src[0] == '\0') - goto fail_match; - - if (src[0] == '"') { - ++src; - break; - } - - if (src[0] == '\\') { - switch (src[1]) { - case '\\': - *(dst++) = '\\'; - src += 2; - break; - case '"': - *(dst++) = '"'; - src += 2; - break; - default: - goto fail_escape; - } - } else { - *(dst++) = *(src++); - } - } - - if (*src != '\0') - return -1; - } - - if (canonicalize_name(buffer)) - goto fail_canon; - return 0; -fail_canon: - fprintf(stderr, "%s: " PRI_SZ ": Malformed filename.\n", - filename, line_no); - return -1; -fail_escape: - fprintf(stderr, "%s: " PRI_SZ ": Unknown escape sequence `\\%c` " - "in filename.\n", filename, line_no, src[1]); - return -1; -fail_match: - fprintf(stderr, "%s: " PRI_SZ ": Unmatched '\"' in filename.\n", - filename, line_no); - return -1; -} - -static int decode_flags(const char *filename, size_t line_no, bool *do_glob, - bool *path_glob, int *flags, char *line) -{ - char *start = line; - - *do_glob = false; - *path_glob = false; - *flags = 0; - - if (*(line++) != '[') - return 0; - - for (;;) { - while (isspace(*line)) - ++line; - - if (*line == ']') { - ++line; - break; - } - - if (strncmp(line, "glob_no_path", 12) == 0) { - line += 12; - *do_glob = true; - *path_glob = false; - } else if (strncmp(line, "glob", 4) == 0) { - line += 4; - *do_glob = true; - *path_glob = true; - } else if (strncmp(line, "dont_fragment", 13) == 0) { - line += 13; - (*flags) |= SQFS_BLK_DONT_FRAGMENT; - } else if (strncmp(line, "align", 5) == 0) { - line += 5; - (*flags) |= SQFS_BLK_ALIGN; - } else if (strncmp(line, "dont_compress", 13) == 0) { - line += 13; - (*flags) |= SQFS_BLK_DONT_COMPRESS; - } else if (strncmp(line, "dont_deduplicate", 16) == 0) { - line += 16; - (*flags) |= SQFS_BLK_DONT_DEDUPLICATE; - } else if (strncmp(line, "nosparse", 8) == 0) { - line += 8; - (*flags) |= SQFS_BLK_IGNORE_SPARSE; - } else { - goto fail_flag; - } - - while (isspace(*line)) - ++line; - - if (*line == ']') { - ++line; - break; - } - - if (*(line++) != ',') - goto fail_sep; - } - - if (!isspace(*line)) - goto fail_fname; - - while (isspace(*line)) - ++line; - - memmove(start, line, strlen(line) + 1); - return 0; -fail_fname: - fprintf(stderr, "%s: " PRI_SZ ": Expected ` ` " - "after flag list.\n", filename, line_no); - return -1; -fail_sep: - fprintf(stderr, "%s: " PRI_SZ ": Unexpected '%c' after flag.\n", - filename, line_no, *line); - return -1; -fail_flag: - fprintf(stderr, "%s: " PRI_SZ ": Unknown flag `%.3s...`.\n", - filename, line_no, line); - return -1; -} - -static void sort_file_list(fstree_t *fs) -{ - file_info_t *out = NULL, *out_last = NULL; - - while (fs->files != NULL) { - sqfs_s64 lowest = fs->files->priority; - file_info_t *it, *prev; - - for (it = fs->files; it != NULL; it = it->next) { - if (it->priority < lowest) - lowest = it->priority; - } - - it = fs->files; - prev = NULL; - - while (it != NULL) { - if (it->priority != lowest) { - prev = it; - it = it->next; - continue; - } - - if (prev == NULL) { - fs->files = it->next; - } else { - prev->next = it->next; - } - - if (out == NULL) { - out = it; - } else { - out_last->next = it; - } - - out_last = it; - it = it->next; - out_last->next = NULL; - } - } - - fs->files = out; -} - -int fstree_sort_files(fstree_t *fs, istream_t *sortfile) -{ - const char *filename; - size_t line_num = 1; - file_info_t *it; - - for (it = fs->files; it != NULL; it = it->next) { - it->priority = 0; - it->flags = 0; - it->already_matched = false; - } - - filename = istream_get_filename(sortfile); - - for (;;) { - bool do_glob, path_glob, have_match; - char *line = NULL; - sqfs_s64 priority; - int ret, flags; - - ret = istream_get_line(sortfile, &line, &line_num, - ISTREAM_LINE_LTRIM | - ISTREAM_LINE_RTRIM | - ISTREAM_LINE_SKIP_EMPTY); - if (ret != 0) { - free(line); - if (ret < 0) - return -1; - break; - } - - if (line[0] == '#') { - free(line); - continue; - } - - if (decode_priority(filename, line_num, line, &priority)) { - free(line); - return -1; - } - - if (decode_flags(filename, line_num, &do_glob, &path_glob, - &flags, line)) { - free(line); - return -1; - } - - if (decode_filename(filename, line_num, line)) { - free(line); - return -1; - } - - have_match = false; - - for (it = fs->files; it != NULL; it = it->next) { - tree_node_t *node; - char *path; - - if (it->already_matched) - continue; - - node = container_of(it, tree_node_t, data.file); - path = fstree_get_path(node); - if (path == NULL) { - fprintf(stderr, "%s: " PRI_SZ ": out-of-memory\n", - filename, line_num); - free(line); - return -1; - } - - if (canonicalize_name(path)) { - fprintf(stderr, - "%s: " PRI_SZ ": [BUG] error " - "reconstructing node path\n", - filename, line_num); - free(line); - free(path); - return -1; - } - - if (do_glob) { - ret = fnmatch(line, path, - path_glob ? FNM_PATHNAME : 0); - - } else { - ret = strcmp(path, line); - } - - free(path); - - if (ret == 0) { - have_match = true; - it->flags = flags; - it->priority = priority; - it->already_matched = true; - - if (!do_glob) - break; - } - } - - if (!have_match) { - fprintf(stderr, "WARNING: %s: " PRI_SZ ": no match " - "for '%s'.\n", - filename, line_num, line); - } - - free(line); - } - - sort_file_list(fs); - return 0; -} diff --git a/bin/gensquashfs/src/dirscan_xattr.c b/bin/gensquashfs/src/dirscan_xattr.c new file mode 100644 index 0000000..7d4e552 --- /dev/null +++ b/bin/gensquashfs/src/dirscan_xattr.c @@ -0,0 +1,220 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * dirscan_xattr.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "mkfs.h" + +#ifdef HAVE_SYS_XATTR_H +static char *get_full_path(const char *prefix, tree_node_t *node) +{ + char *path = NULL, *new = NULL; + size_t path_len, prefix_len; + int ret; + + path = fstree_get_path(node); + if (path == NULL) + goto fail; + + ret = canonicalize_name(path); + assert(ret == 0); + + path_len = strlen(path); + prefix_len = strlen(prefix); + + while (prefix_len > 0 && prefix[prefix_len - 1] == '/') + --prefix_len; + + if (prefix_len > 0) { + new = realloc(path, path_len + prefix_len + 2); + if (new == NULL) + goto fail; + + path = new; + + memmove(path + prefix_len + 1, path, path_len + 1); + memcpy(path, prefix, prefix_len); + path[prefix_len] = '/'; + } + + return path; +fail: + perror("getting full path for xattr scan"); + free(path); + return NULL; +} + +static int xattr_from_path(sqfs_xattr_writer_t *xwr, const char *path) +{ + char *key, *value = NULL, *buffer = NULL; + ssize_t buflen, vallen, keylen; + int ret; + + buflen = llistxattr(path, NULL, 0); + if (buflen < 0) { + fprintf(stderr, "llistxattr %s: %s", path, strerror(errno)); + return -1; + } + + if (buflen == 0) + return 0; + + buffer = malloc(buflen); + if (buffer == NULL) { + perror("xattr name buffer"); + return -1; + } + + buflen = llistxattr(path, buffer, buflen); + if (buflen == -1) { + fprintf(stderr, "llistxattr %s: %s", path, strerror(errno)); + goto fail; + } + + key = buffer; + while (buflen > 0) { + vallen = lgetxattr(path, key, NULL, 0); + if (vallen == -1) { + fprintf(stderr, "lgetxattr %s: %s", + path, strerror(errno)); + goto fail; + } + + if (vallen > 0) { + value = calloc(1, vallen); + if (value == NULL) { + perror("allocating xattr value buffer"); + goto fail; + } + + vallen = lgetxattr(path, key, value, vallen); + if (vallen == -1) { + fprintf(stderr, "lgetxattr %s: %s\n", + path, strerror(errno)); + goto fail; + } + + ret = sqfs_xattr_writer_add(xwr, key, value, vallen); + if (ret) { + sqfs_perror(path, + "storing xattr key-value pairs", + ret); + goto fail; + } + + free(value); + value = NULL; + } + + keylen = strlen(key) + 1; + buflen -= keylen; + key += keylen; + } + + free(buffer); + return 0; +fail: + free(value); + free(buffer); + return -1; +} +#endif + +static int xattr_xcan_dfs(const char *path_prefix, void *selinux_handle, + sqfs_xattr_writer_t *xwr, bool scan_xattr, void *xattr_map, + tree_node_t *node) +{ + char *path = NULL; + int ret; + + ret = sqfs_xattr_writer_begin(xwr, 0); + if (ret) { + sqfs_perror(node->name, "recoding xattr key-value pairs\n", + ret); + return -1; + } + +#ifdef HAVE_SYS_XATTR_H + if (scan_xattr) { + path = get_full_path(path_prefix, node); + if (path == NULL) + return -1; + + ret = xattr_from_path(xwr, path); + free(path); + path = NULL; + if (ret) { + ret = -1; + goto out; + } + } +#else + (void)path_prefix; +#endif + + if (selinux_handle != NULL || xattr_map != NULL) { + path = fstree_get_path(node); + + if (path == NULL) { + perror("reconstructing absolute path"); + ret = -1; + goto out; + } + } + + if (xattr_map != NULL) { + ret = xattr_apply_map_file(path, xattr_map, xwr); + + if (ret) { + ret = -1; + goto out; + } + } + + if (selinux_handle != NULL) { + ret = selinux_relable_node(selinux_handle, xwr, node, path); + + if (ret) { + ret = -1; + goto out; + } + } + + if (sqfs_xattr_writer_end(xwr, &node->xattr_idx)) { + sqfs_perror(node->name, "completing xattr key-value pairs", + ret); + ret = -1; + goto out; + } + + if (S_ISDIR(node->mode)) { + node = node->data.dir.children; + + while (node != NULL) { + if (xattr_xcan_dfs(path_prefix, selinux_handle, xwr, + scan_xattr, xattr_map, node)) { + ret = -1; + goto out; + } + + node = node->next; + } + } + +out: + free(path); + return ret; +} + +int xattrs_from_dir(fstree_t *fs, const char *path, void *selinux_handle, + void *xattr_map, sqfs_xattr_writer_t *xwr, bool scan_xattr) +{ + if (xwr == NULL) + return 0; + + if (selinux_handle == NULL && !scan_xattr && xattr_map == NULL) + return 0; + + return xattr_xcan_dfs(path, selinux_handle, xwr, scan_xattr, xattr_map, fs->root); +} diff --git a/bin/gensquashfs/src/filemap_xattr.c b/bin/gensquashfs/src/filemap_xattr.c new file mode 100644 index 0000000..dd76b50 --- /dev/null +++ b/bin/gensquashfs/src/filemap_xattr.c @@ -0,0 +1,254 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * filemap_xattr.c + * + * Copyright (C) 2022 Enno Boland + */ +#include "fstree.h" +#include "mkfs.h" +#include + +#define NEW_FILE_START "# file: " + +static void print_error(const char *filename, size_t line_num, const char *err) +{ + fprintf(stderr, "%s: " PRI_SZ ": %s\n", filename, line_num, err); +} + +// Taken from attr-2.5.1/tools/setfattr.c +static sqfs_u8 *decode(const char *filename, size_t line_num, + const char *value, size_t *size) +{ + sqfs_u8 *decoded = NULL; + + if (*size == 0) { + decoded = (sqfs_u8 *)strdup(""); + if (decoded == NULL) + goto fail_alloc; + return decoded; + } + + if (value[0] == '0' && (value[1] == 'x' || value[1] == 'X')) { + *size = ((*size) - 2) / 2; + + decoded = calloc(1, (*size) + 1); + if (decoded == NULL) + goto fail_alloc; + + if (hex_decode(value + 2, (*size) * 2, decoded, *size)) + goto fail_encode; + } else if (value[0] == '0' && (value[1] == 's' || value[1] == 'S')) { + size_t input_len = *size - 2; + + *size = (input_len / 4) * 3; + + decoded = calloc(1, (*size) + 1); + if (decoded == NULL) + goto fail_alloc; + + if (base64_decode(value + 2, input_len, decoded, size)) + goto fail_encode; + } else { + const char *v = value, *end = value + *size; + sqfs_u8 *d; + + if (end > v + 1 && *v == '"' && *(end - 1) == '"') { + v++; + end--; + } + + decoded = calloc(1, (*size) + 1); + if (decoded == NULL) + goto fail_alloc; + + d = decoded; + + while (v < end) { + if (v[0] == '\\') { + if (v[1] == '\\' || v[1] == '"') { + *d++ = *++v; + v++; + } else if (v[1] >= '0' && v[1] <= '7') { + int c = 0; + v++; + c = (*v++ - '0'); + if (*v >= '0' && *v <= '7') + c = (c << 3) + (*v++ - '0'); + if (*v >= '0' && *v <= '7') + c = (c << 3) + (*v++ - '0'); + *d++ = c; + } else + *d++ = *v++; + } else + *d++ = *v++; + } + *size = d - decoded; + } + return decoded; +fail_alloc: + fprintf(stderr, "out of memory\n"); + return NULL; +fail_encode: + print_error(filename, line_num, "bad input encoding"); + free(decoded); + return NULL; +} + +static int parse_file_name(const char *filename, size_t line_num, + char *line, struct XattrMap *map) +{ + struct XattrMapPattern *current_file; + char *file_name = strdup(line + strlen(NEW_FILE_START)); + + if (file_name == NULL) + goto fail_alloc; + + current_file = calloc(1, sizeof(struct XattrMapPattern)); + if (current_file == NULL) + goto fail_alloc; + + current_file->next = map->patterns; + map->patterns = current_file; + + if (canonicalize_name(file_name)) { + print_error(filename, line_num, "invalid absolute path"); + free(current_file); + free(file_name); + return -1; + } + + current_file->path = file_name; + return 0; +fail_alloc: + fprintf(stderr, "out of memory\n"); + free(file_name); + return -1; +} + +static int parse_xattr(const char *filename, size_t line_num, char *key_start, + char *value_start, struct XattrMap *map) +{ + size_t len; + struct XattrMapPattern *current_pattern = map->patterns; + struct XattrMapEntry *current_entry; + + if (current_pattern == NULL) { + print_error(filename, line_num, "no file specified yet"); + return -1; + } + + current_entry = calloc(1, sizeof(struct XattrMapEntry)); + if (current_entry == NULL) { + return -1; + } + current_entry->next = current_pattern->entries; + current_pattern->entries = current_entry; + + current_entry->key = strdup(key_start); + len = strlen(value_start); + current_entry->value = decode(filename, line_num, value_start, &len); + current_entry->value_len = len; + + return 0; +} + +void * +xattr_open_map_file(const char *path) { + struct XattrMap *map; + size_t line_num = 1; + char *p = NULL; + istream_t *file = istream_open_file(path); + if (file == NULL) { + return NULL; + } + + map = calloc(1, sizeof(struct XattrMap)); + if (map == NULL) + goto fail_close; + + for (;;) { + char *line = NULL; + int ret = istream_get_line(file, &line, &line_num, + ISTREAM_LINE_LTRIM | + ISTREAM_LINE_RTRIM | + ISTREAM_LINE_SKIP_EMPTY); + if (ret < 0) + goto fail; + if (ret > 0) + break; + + if (strncmp(NEW_FILE_START, line, strlen(NEW_FILE_START)) == 0) { + ret = parse_file_name(path, line_num, line, map); + } else if ((p = strchr(line, '='))) { + *(p++) = '\0'; + ret = parse_xattr(path, line_num, line, p, map); + } else if (line[0] != '#') { + print_error(path, line_num, "not a key-value pair"); + ret = -1; + } + + ++line_num; + free(line); + if (ret < 0) + goto fail; + } + + sqfs_drop(file); + return map; +fail: + xattr_close_map_file(map); +fail_close: + sqfs_drop(file); + return NULL; +} + +void +xattr_close_map_file(void *xattr_map) { + struct XattrMap *map = xattr_map; + while (map->patterns != NULL) { + struct XattrMapPattern *file = map->patterns; + map->patterns = file->next; + while (file->entries != NULL) { + struct XattrMapEntry *entry = file->entries; + file->entries = entry->next; + free(entry->key); + free(entry->value); + free(entry); + } + free(file->path); + free(file); + } + free(xattr_map); +} + +int +xattr_apply_map_file(char *path, void *map, sqfs_xattr_writer_t *xwr) { + struct XattrMap *xattr_map = map; + int ret = 0; + const struct XattrMapPattern *pat; + const struct XattrMapEntry *entry; + + for (pat = xattr_map->patterns; pat != NULL; pat = pat->next) { + char *patstr = pat->path; + const char *stripped = path; + + if (patstr[0] != '/' && stripped[0] == '/') { + stripped++; + } + + if (strcmp(patstr, stripped) == 0) { + printf("Applying xattrs for %s", path); + for (entry = pat->entries; entry != NULL; entry = entry->next) { + printf(" %s = \n", entry->key); + fwrite(entry->value, entry->value_len, 1, stdout); + puts("\n"); + ret = sqfs_xattr_writer_add( + xwr, entry->key, entry->value, entry->value_len); + if (ret < 0) { + return ret; + } + } + } + } + return ret; +} diff --git a/bin/gensquashfs/src/fstree_from_dir.c b/bin/gensquashfs/src/fstree_from_dir.c new file mode 100644 index 0000000..5b3f003 --- /dev/null +++ b/bin/gensquashfs/src/fstree_from_dir.c @@ -0,0 +1,493 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * fstree_from_dir.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "config.h" +#include "mkfs.h" + +#include +#include +#include +#include + +#if defined(_WIN32) || defined(__WINDOWS__) +#define UNIX_EPOCH_ON_W32 11644473600UL +#define W32_TICS_PER_SEC 10000000UL + +static sqfs_u32 w32time_to_sqfs_time(const FILETIME *ft) +{ + sqfs_u64 w32ts; + + w32ts = ft->dwHighDateTime; + w32ts <<= 32UL; + w32ts |= ft->dwLowDateTime; + + w32ts /= W32_TICS_PER_SEC; + + if (w32ts <= UNIX_EPOCH_ON_W32) + return 0; + + w32ts -= UNIX_EPOCH_ON_W32; + + return (w32ts < 0x0FFFFFFFFUL) ? w32ts : 0xFFFFFFFF; +} + +static int add_node(fstree_t *fs, tree_node_t *root, + scan_node_callback cb, void *user, + unsigned int flags, + const LPWIN32_FIND_DATAW entry) +{ + tree_node_t *n; + DWORD length; + + if (entry->cFileName[0] == '.') { + if (entry->cFileName[1] == '\0') + return 0; + + if (entry->cFileName[1] == '.' && entry->cFileName[2] == '\0') + return 0; + } + + length = WideCharToMultiByte(CP_UTF8, 0, entry->cFileName, + -1, NULL, 0, NULL, NULL); + if (length <= 0) { + w32_perror("converting path to UTF-8"); + return -1; + } + + n = calloc(1, sizeof(*n) + length + 1); + if (n == NULL) { + fprintf(stderr, "creating tree node: out-of-memory\n"); + return -1; + } + + n->name = (char *)n->payload; + WideCharToMultiByte(CP_UTF8, 0, entry->cFileName, -1, + n->name, length + 1, NULL, NULL); + + if (entry->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + if (flags & DIR_SCAN_NO_DIR) { + free(n); + return 0; + } + + n->mode = S_IFDIR | 0755; + } else { + if (flags & DIR_SCAN_NO_FILE) { + free(n); + return 0; + } + + n->mode = S_IFREG | 0644; + } + + if (cb != NULL) { + int ret = cb(user, fs, n); + + if (ret != 0) { + free(n); + return ret < 0 ? ret : 0; + } + } + + if (flags & DIR_SCAN_KEEP_TIME) { + n->mod_time = w32time_to_sqfs_time(&(entry->ftLastWriteTime)); + } else { + n->mod_time = fs->defaults.st_mtime; + } + + fstree_insert_sorted(root, n); + return 0; +} + +static int scan_dir(fstree_t *fs, tree_node_t *root, + const char *path, const WCHAR *wpath, + scan_node_callback cb, void *user, + unsigned int flags) +{ + WIN32_FIND_DATAW entry; + HANDLE dirhnd; + + dirhnd = FindFirstFileW(wpath, &entry); + + if (dirhnd == INVALID_HANDLE_VALUE) + goto fail_perror; + + do { + if (add_node(fs, root, cb, user, flags, &entry)) + goto fail; + } while (FindNextFileW(dirhnd, &entry)); + + if (GetLastError() != ERROR_NO_MORE_FILES) + goto fail_perror; + + FindClose(dirhnd); + return 0; +fail_perror: + w32_perror(path); +fail: + if (dirhnd != INVALID_HANDLE_VALUE) + FindClose(dirhnd); + return -1; +} + +int fstree_from_dir(fstree_t *fs, tree_node_t *root, + const char *path, scan_node_callback cb, + void *user, unsigned int flags) +{ + WCHAR *wpath = NULL, *new = NULL; + size_t len, newlen; + tree_node_t *n; + + /* path -> to_wchar(path) + L"\*" */ + wpath = path_to_windows(path); + if (wpath == NULL) { + fprintf(stderr, "%s: allocation failure.\n", path); + return -1; + } + + for (len = 0; wpath[len] != '\0'; ++len) + ; + + newlen = len + 1; + + if (len > 0 && wpath[len - 1] != '\\') + newlen += 1; + + new = realloc(wpath, sizeof(wpath[0]) * (newlen + 1)); + if (new == NULL) { + fprintf(stderr, "%s: allocation failure.\n", path); + goto fail; + } + + wpath = new; + + if (len > 0 && wpath[len - 1] != '\\') + wpath[len++] = '\\'; + + wpath[len++] = '*'; + wpath[len++] = '\0'; + + /* scan directory contents */ + if (scan_dir(fs, root, path, wpath, cb, user, flags)) + goto fail; + + free(wpath); + wpath = NULL; + + /* recursion step */ + if (flags & DIR_SCAN_NO_RECURSION) + return 0; + + for (n = root->data.dir.children; n != NULL; n = n->next) { + if (!S_ISDIR(n->mode)) + continue; + + if (fstree_from_subdir(fs, n, path, n->name, cb, user, flags)) + return -1; + } + + return 0; +fail: + free(wpath); + return -1; +} + +int fstree_from_subdir(fstree_t *fs, tree_node_t *root, + const char *path, const char *subdir, + scan_node_callback cb, void *user, + unsigned int flags) +{ + size_t len, plen, slen; + WCHAR *wpath = NULL; + char *temp = NULL; + tree_node_t *n; + + plen = strlen(path); + slen = subdir == NULL ? 0 : strlen(subdir); + + if (slen == 0) + return fstree_from_dir(fs, root, path, cb, user, flags); + + len = plen + 1 + slen + 2; + + temp = calloc(1, len + 1); + if (temp == NULL) { + fprintf(stderr, "%s/%s: allocation failure.\n", path, subdir); + return -1; + } + + memcpy(temp, path, plen); + temp[plen] = '/'; + memcpy(temp + plen + 1, subdir, slen); + temp[plen + 1 + slen ] = '/'; + temp[plen + 1 + slen + 1] = '*'; + temp[plen + 1 + slen + 2] = '\0'; + + wpath = path_to_windows(temp); + if (wpath == NULL) { + fprintf(stderr, "%s: allocation failure.\n", temp); + goto fail; + } + + if (scan_dir(fs, root, temp, wpath, cb, user, flags)) + goto fail; + + free(wpath); + wpath = NULL; + + if (flags & DIR_SCAN_NO_RECURSION) { + free(temp); + return 0; + } + + temp[plen + 1 + slen] = '\0'; + + for (n = root->data.dir.children; n != NULL; n = n->next) { + if (!S_ISDIR(n->mode)) + continue; + + if (fstree_from_subdir(fs, n, temp, n->name, cb, user, flags)) + goto fail; + } + + free(temp); + return 0; +fail: + free(temp); + free(wpath); + return -1; + +} +#else +static void discard_node(tree_node_t *root, tree_node_t *n) +{ + tree_node_t *it; + + if (n == root->data.dir.children) { + root->data.dir.children = n->next; + } else { + it = root->data.dir.children; + + while (it != NULL && it->next != n) + it = it->next; + + if (it != NULL) + it->next = n->next; + } + + free(n); +} + +static int populate_dir(int dir_fd, fstree_t *fs, tree_node_t *root, + dev_t devstart, scan_node_callback cb, + void *user, unsigned int flags) +{ + char *extra = NULL; + struct dirent *ent; + int ret, childfd; + struct stat sb; + tree_node_t *n; + DIR *dir; + + dir = fdopendir(dir_fd); + if (dir == NULL) { + perror("fdopendir"); + close(dir_fd); + return -1; + } + + /* XXX: fdopendir can dup and close dir_fd internally + and still be compliant with the spec. */ + dir_fd = dirfd(dir); + + for (;;) { + errno = 0; + ent = readdir(dir); + + if (ent == NULL) { + if (errno) { + perror("readdir"); + goto fail; + } + break; + } + + if (!strcmp(ent->d_name, "..") || !strcmp(ent->d_name, ".")) + continue; + + if (fstatat(dir_fd, ent->d_name, &sb, AT_SYMLINK_NOFOLLOW)) { + perror(ent->d_name); + goto fail; + } + + switch (sb.st_mode & S_IFMT) { + case S_IFSOCK: + if (flags & DIR_SCAN_NO_SOCK) + continue; + break; + case S_IFLNK: + if (flags & DIR_SCAN_NO_SLINK) + continue; + break; + case S_IFREG: + if (flags & DIR_SCAN_NO_FILE) + continue; + break; + case S_IFBLK: + if (flags & DIR_SCAN_NO_BLK) + continue; + break; + case S_IFCHR: + if (flags & DIR_SCAN_NO_CHR) + continue; + break; + case S_IFIFO: + if (flags & DIR_SCAN_NO_FIFO) + continue; + break; + default: + break; + } + + if ((flags & DIR_SCAN_ONE_FILESYSTEM) && sb.st_dev != devstart) + continue; + + if (S_ISLNK(sb.st_mode)) { + size_t size; + + if ((sizeof(sb.st_size) > sizeof(size_t)) && + sb.st_size > SIZE_MAX) { + errno = EOVERFLOW; + goto fail_rdlink; + } + + if (SZ_ADD_OV((size_t)sb.st_size, 1, &size)) { + errno = EOVERFLOW; + goto fail_rdlink; + } + + extra = calloc(1, size); + if (extra == NULL) + goto fail_rdlink; + + if (readlinkat(dir_fd, ent->d_name, + extra, (size_t)sb.st_size) < 0) { + goto fail_rdlink; + } + + extra[sb.st_size] = '\0'; + } + + if (!(flags & DIR_SCAN_KEEP_TIME)) + sb.st_mtime = fs->defaults.st_mtime; + + if (S_ISDIR(sb.st_mode) && (flags & DIR_SCAN_NO_DIR)) { + n = fstree_get_node_by_path(fs, root, ent->d_name, + false, false); + if (n == NULL) + continue; + + ret = 0; + } else { + n = fstree_mknode(root, ent->d_name, + strlen(ent->d_name), extra, &sb); + if (n == NULL) { + perror("creating tree node"); + goto fail; + } + + ret = (cb == NULL) ? 0 : cb(user, fs, n); + } + + free(extra); + extra = NULL; + + if (ret < 0) + goto fail; + + if (ret > 0) { + discard_node(root, n); + continue; + } + + if (S_ISDIR(n->mode) && !(flags & DIR_SCAN_NO_RECURSION)) { + childfd = openat(dir_fd, n->name, O_DIRECTORY | + O_RDONLY | O_CLOEXEC); + if (childfd < 0) { + perror(n->name); + goto fail; + } + + if (populate_dir(childfd, fs, n, devstart, + cb, user, flags)) { + goto fail; + } + } + } + + closedir(dir); + return 0; +fail_rdlink: + perror("readlink"); +fail: + closedir(dir); + free(extra); + return -1; +} + +int fstree_from_subdir(fstree_t *fs, tree_node_t *root, + const char *path, const char *subdir, + scan_node_callback cb, void *user, + unsigned int flags) +{ + struct stat sb; + int fd, subfd; + + if (!S_ISDIR(root->mode)) { + fprintf(stderr, + "scanning %s/%s into %s: target is not a directory\n", + path, subdir == NULL ? "" : subdir, root->name); + return -1; + } + + fd = open(path, O_DIRECTORY | O_RDONLY | O_CLOEXEC); + if (fd < 0) { + perror(path); + return -1; + } + + if (subdir != NULL) { + subfd = openat(fd, subdir, O_DIRECTORY | O_RDONLY | O_CLOEXEC); + + if (subfd < 0) { + fprintf(stderr, "%s/%s: %s\n", path, subdir, + strerror(errno)); + close(fd); + return -1; + } + + close(fd); + fd = subfd; + } + + if (fstat(fd, &sb)) { + fprintf(stderr, "%s/%s: %s\n", path, + subdir == NULL ? "" : subdir, + strerror(errno)); + close(fd); + return -1; + } + + return populate_dir(fd, fs, root, sb.st_dev, cb, user, flags); +} + +int fstree_from_dir(fstree_t *fs, tree_node_t *root, + const char *path, scan_node_callback cb, + void *user, unsigned int flags) +{ + return fstree_from_subdir(fs, root, path, NULL, cb, user, flags); +} +#endif diff --git a/bin/gensquashfs/src/fstree_from_file.c b/bin/gensquashfs/src/fstree_from_file.c new file mode 100644 index 0000000..e26d4b1 --- /dev/null +++ b/bin/gensquashfs/src/fstree_from_file.c @@ -0,0 +1,591 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * fstree_from_file.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "config.h" + +#include "util/util.h" +#include "io/file.h" +#include "compat.h" +#include "mkfs.h" + +#include +#include +#include +#include +#include + +struct glob_context { + const char *filename; + size_t line_num; + + struct stat *basic; + unsigned int glob_flags; + + char *name_pattern; +}; + +enum { + GLOB_MODE_FROM_SRC = 0x01, + GLOB_UID_FROM_SRC = 0x02, + GLOB_GID_FROM_SRC = 0x04, + GLOB_FLAG_PATH = 0x08, +}; + +static const struct { + const char *name; + unsigned int clear_flag; + unsigned int set_flag; +} glob_scan_flags[] = { + { "-type b", DIR_SCAN_NO_BLK, 0 }, + { "-type c", DIR_SCAN_NO_CHR, 0 }, + { "-type d", DIR_SCAN_NO_DIR, 0 }, + { "-type p", DIR_SCAN_NO_FIFO, 0 }, + { "-type f", DIR_SCAN_NO_FILE, 0 }, + { "-type l", DIR_SCAN_NO_SLINK, 0 }, + { "-type s", DIR_SCAN_NO_SOCK, 0 }, + { "-xdev", 0, DIR_SCAN_ONE_FILESYSTEM }, + { "-mount", 0, DIR_SCAN_ONE_FILESYSTEM }, + { "-keeptime", 0, DIR_SCAN_KEEP_TIME }, + { "-nonrecursive", 0, DIR_SCAN_NO_RECURSION }, +}; + +static int add_generic(fstree_t *fs, const char *filename, size_t line_num, + const char *path, struct stat *sb, + const char *basepath, unsigned int glob_flags, + const char *extra) +{ + (void)basepath; + (void)glob_flags; + + if (fstree_add_generic(fs, path, sb, extra) == NULL) { + fprintf(stderr, "%s: " PRI_SZ ": %s: %s\n", + filename, line_num, path, strerror(errno)); + return -1; + } + + return 0; +} + +static int add_device(fstree_t *fs, const char *filename, size_t line_num, + const char *path, struct stat *sb, const char *basepath, + unsigned int glob_flags, const char *extra) +{ + unsigned int maj, min; + char c; + + if (sscanf(extra, "%c %u %u", &c, &maj, &min) != 3) { + fprintf(stderr, "%s: " PRI_SZ ": " + "expected ' major minor'\n", + filename, line_num); + return -1; + } + + if (c == 'c' || c == 'C') { + sb->st_mode |= S_IFCHR; + } else if (c == 'b' || c == 'B') { + sb->st_mode |= S_IFBLK; + } else { + fprintf(stderr, "%s: " PRI_SZ ": unknown device type '%c'\n", + filename, line_num, c); + return -1; + } + + sb->st_rdev = makedev(maj, min); + return add_generic(fs, filename, line_num, path, sb, basepath, + glob_flags, NULL); +} + +static int add_file(fstree_t *fs, const char *filename, size_t line_num, + const char *path, struct stat *basic, const char *basepath, + unsigned int glob_flags, const char *extra) +{ + if (extra == NULL || *extra == '\0') + extra = path; + + return add_generic(fs, filename, line_num, path, basic, + basepath, glob_flags, extra); +} + +static int add_hard_link(fstree_t *fs, const char *filename, size_t line_num, + const char *path, struct stat *basic, + const char *basepath, unsigned int glob_flags, + const char *extra) +{ + (void)basepath; + (void)glob_flags; + (void)basic; + + if (fstree_add_hard_link(fs, path, extra) == NULL) { + fprintf(stderr, "%s: " PRI_SZ ": %s\n", + filename, line_num, strerror(errno)); + return -1; + } + return 0; +} + +static int glob_node_callback(void *user, fstree_t *fs, tree_node_t *node) +{ + struct glob_context *ctx = user; + char *path; + int ret; + (void)fs; + + if (!(ctx->glob_flags & GLOB_MODE_FROM_SRC)) { + node->mode &= ~(07777); + node->mode |= ctx->basic->st_mode & 07777; + } + + if (!(ctx->glob_flags & GLOB_UID_FROM_SRC)) + node->uid = ctx->basic->st_uid; + + if (!(ctx->glob_flags & GLOB_GID_FROM_SRC)) + node->gid = ctx->basic->st_gid; + + if (ctx->name_pattern != NULL) { + if (ctx->glob_flags & GLOB_FLAG_PATH) { + path = fstree_get_path(node); + if (path == NULL) { + fprintf(stderr, "%s: " PRI_SZ ": %s\n", + ctx->filename, ctx->line_num, + strerror(errno)); + return -1; + } + + ret = canonicalize_name(path); + assert(ret == 0); + + ret = fnmatch(ctx->name_pattern, path, FNM_PATHNAME); + free(path); + } else { + ret = fnmatch(ctx->name_pattern, node->name, 0); + } + + if (ret != 0) + return 1; + } + + return 0; +} + +static size_t name_string_length(const char *str) +{ + size_t len = 0; + int start; + + if (*str == '"' || *str == '\'') { + start = *str; + ++len; + + while (str[len] != '\0' && str[len] != start) + ++len; + + if (str[len] == start) + ++len; + } else { + while (str[len] != '\0' && !isspace(str[len])) + ++len; + } + + return len; +} + +static void quote_remove(char *str) +{ + char *dst = str; + int start = *(str++); + + if (start != '\'' && start != '"') + return; + + while (*str != start && *str != '\0') + *(dst++) = *(str++); + + *(dst++) = '\0'; +} + +static int glob_files(fstree_t *fs, const char *filename, size_t line_num, + const char *path, struct stat *basic, + const char *basepath, unsigned int glob_flags, + const char *extra) +{ + unsigned int scan_flags = 0, all_flags; + struct glob_context ctx; + bool first_clear_flag; + size_t i, count, len; + tree_node_t *root; + int ret; + + memset(&ctx, 0, sizeof(ctx)); + ctx.filename = filename; + ctx.line_num = line_num; + ctx.basic = basic; + ctx.glob_flags = glob_flags; + + /* fetch the actual target node */ + root = fstree_get_node_by_path(fs, fs->root, path, true, false); + if (root == NULL) { + fprintf(stderr, "%s: " PRI_SZ ": %s: %s\n", + filename, line_num, path, strerror(errno)); + return -1; + } + + /* process options */ + first_clear_flag = true; + + all_flags = DIR_SCAN_NO_BLK | DIR_SCAN_NO_CHR | DIR_SCAN_NO_DIR | + DIR_SCAN_NO_FIFO | DIR_SCAN_NO_FILE | DIR_SCAN_NO_SLINK | + DIR_SCAN_NO_SOCK; + + while (extra != NULL && *extra != '\0') { + count = sizeof(glob_scan_flags) / sizeof(glob_scan_flags[0]); + + for (i = 0; i < count; ++i) { + len = strlen(glob_scan_flags[i].name); + if (strncmp(extra, glob_scan_flags[i].name, len) != 0) + continue; + + if (isspace(extra[len])) { + extra += len; + while (isspace(*extra)) + ++extra; + break; + } + } + + if (i < count) { + if (glob_scan_flags[i].clear_flag != 0 && + first_clear_flag) { + scan_flags |= all_flags; + first_clear_flag = false; + } + + scan_flags &= ~(glob_scan_flags[i].clear_flag); + scan_flags |= glob_scan_flags[i].set_flag; + continue; + } + + if (strncmp(extra, "-name", 5) == 0 && isspace(extra[5])) { + for (extra += 5; isspace(*extra); ++extra) + ; + + len = name_string_length(extra); + + free(ctx.name_pattern); + ctx.name_pattern = strndup(extra, len); + extra += len; + + while (isspace(*extra)) + ++extra; + + quote_remove(ctx.name_pattern); + continue; + } + + if (strncmp(extra, "-path", 5) == 0 && isspace(extra[5])) { + for (extra += 5; isspace(*extra); ++extra) + ; + + len = name_string_length(extra); + + free(ctx.name_pattern); + ctx.name_pattern = strndup(extra, len); + extra += len; + + while (isspace(*extra)) + ++extra; + + quote_remove(ctx.name_pattern); + ctx.glob_flags |= GLOB_FLAG_PATH; + continue; + } + + if (extra[0] == '-') { + if (extra[1] == '-' && isspace(extra[2])) { + extra += 2; + while (isspace(*extra)) + ++extra; + break; + } + + fprintf(stderr, "%s: " PRI_SZ ": unknown option.\n", + filename, line_num); + free(ctx.name_pattern); + return -1; + } else { + break; + } + } + + if (extra != NULL && *extra == '\0') + extra = NULL; + + /* do the scan */ + if (basepath == NULL) { + if (extra == NULL) { + ret = fstree_from_dir(fs, root, ".", glob_node_callback, + &ctx, scan_flags); + } else { + ret = fstree_from_dir(fs, root, extra, + glob_node_callback, + &ctx, scan_flags); + } + } else { + ret = fstree_from_subdir(fs, root, basepath, extra, + glob_node_callback, &ctx, + scan_flags); + } + + free(ctx.name_pattern); + return ret; +} + +static const struct callback_t { + const char *keyword; + unsigned int mode; + bool need_extra; + bool is_glob; + bool allow_root; + int (*callback)(fstree_t *fs, const char *filename, size_t line_num, + const char *path, struct stat *sb, + const char *basepath, unsigned int glob_flags, + const char *extra); +} file_list_hooks[] = { + { "dir", S_IFDIR, false, false, true, add_generic }, + { "slink", S_IFLNK, true, false, false, add_generic }, + { "link", 0, true, false, false, add_hard_link }, + { "nod", 0, true, false, false, add_device }, + { "pipe", S_IFIFO, false, false, false, add_generic }, + { "sock", S_IFSOCK, false, false, false, add_generic }, + { "file", S_IFREG, false, false, false, add_file }, + { "glob", 0, false, true, true, glob_files }, +}; + +#define NUM_HOOKS (sizeof(file_list_hooks) / sizeof(file_list_hooks[0])) + +static char *skip_space(char *str) +{ + if (!isspace(*str)) + return NULL; + while (isspace(*str)) + ++str; + return str; +} + +static char *read_u32(char *str, sqfs_u32 *out, sqfs_u32 base) +{ + *out = 0; + + if (!isdigit(*str)) + return NULL; + + while (isdigit(*str)) { + sqfs_u32 x = *(str++) - '0'; + + if (x >= base || (*out) > (0xFFFFFFFF - x) / base) + return NULL; + + (*out) = (*out) * base + x; + } + + return str; +} + +static char *read_str(char *str, char **out) +{ + *out = str; + + if (*str == '"') { + char *ptr = str++; + + while (*str != '\0' && *str != '"') { + if (str[0] == '\\' && + (str[1] == '"' || str[1] == '\\')) { + *(ptr++) = str[1]; + str += 2; + } else { + *(ptr++) = *(str++); + } + } + + if (str[0] != '"' || !isspace(str[1])) + return NULL; + + *ptr = '\0'; + ++str; + } else { + while (*str != '\0' && !isspace(*str)) + ++str; + + if (!isspace(*str)) + return NULL; + + *(str++) = '\0'; + } + + while (isspace(*str)) + ++str; + + return str; +} + +static int handle_line(fstree_t *fs, const char *filename, + size_t line_num, char *line, + const char *basepath) +{ + const char *extra = NULL, *msg = NULL; + const struct callback_t *cb = NULL; + unsigned int glob_flags = 0; + sqfs_u32 uid, gid, mode; + struct stat sb; + char *path; + + for (size_t i = 0; i < NUM_HOOKS; ++i) { + size_t len = strlen(file_list_hooks[i].keyword); + if (strncmp(file_list_hooks[i].keyword, line, len) != 0) + continue; + + if (isspace(line[len])) { + cb = file_list_hooks + i; + line = skip_space(line + len); + break; + } + } + + if (cb == NULL) + goto fail_kw; + + if ((line = read_str(line, &path)) == NULL) + goto fail_ent; + + if (canonicalize_name(path)) + goto fail_ent; + + if (*path == '\0' && !cb->allow_root) + goto fail_root; + + if (cb->is_glob && *line == '*') { + ++line; + mode = 0; + glob_flags |= GLOB_MODE_FROM_SRC; + } else { + if ((line = read_u32(line, &mode, 8)) == NULL || mode > 07777) + goto fail_mode; + } + + if ((line = skip_space(line)) == NULL) + goto fail_ent; + + if (cb->is_glob && *line == '*') { + ++line; + uid = 0; + glob_flags |= GLOB_UID_FROM_SRC; + } else { + if ((line = read_u32(line, &uid, 10)) == NULL) + goto fail_uid_gid; + } + + if ((line = skip_space(line)) == NULL) + goto fail_ent; + + if (cb->is_glob && *line == '*') { + ++line; + gid = 0; + glob_flags |= GLOB_GID_FROM_SRC; + } else { + if ((line = read_u32(line, &gid, 10)) == NULL) + goto fail_uid_gid; + } + + if ((line = skip_space(line)) != NULL && *line != '\0') + extra = line; + + if (cb->need_extra && extra == NULL) + goto fail_no_extra; + + /* forward to callback */ + memset(&sb, 0, sizeof(sb)); + sb.st_mtime = fs->defaults.st_mtime; + sb.st_mode = mode | cb->mode; + sb.st_uid = uid; + sb.st_gid = gid; + + return cb->callback(fs, filename, line_num, path, + &sb, basepath, glob_flags, extra); +fail_root: + fprintf(stderr, "%s: " PRI_SZ ": cannot use / as argument for %s.\n", + filename, line_num, cb->keyword); + return -1; +fail_no_extra: + fprintf(stderr, "%s: " PRI_SZ ": missing argument for %s.\n", + filename, line_num, cb->keyword); + return -1; +fail_uid_gid: + msg = "uid & gid must be decimal numbers < 2^32"; + goto out_desc; +fail_mode: + msg = "mode must be an octal number <= 07777"; + goto out_desc; +fail_kw: + msg = "unknown entry type"; + goto out_desc; +fail_ent: + msg = "error in entry description"; + goto out_desc; +out_desc: + fprintf(stderr, "%s: " PRI_SZ ": %s.\n", filename, line_num, msg); + fputs("expected: []\n", + stderr); + return -1; +} + +int fstree_from_file_stream(fstree_t *fs, istream_t *fp, const char *basepath) +{ + const char *filename; + size_t line_num = 1; + char *line; + int ret; + + filename = istream_get_filename(fp); + + for (;;) { + ret = istream_get_line(fp, &line, &line_num, + ISTREAM_LINE_LTRIM | ISTREAM_LINE_SKIP_EMPTY); + if (ret < 0) + return -1; + if (ret > 0) + break; + + if (line[0] != '#') { + if (handle_line(fs, filename, line_num, + line, basepath)) { + goto fail_line; + } + } + + free(line); + ++line_num; + } + + return 0; +fail_line: + free(line); + return -1; +} + +int fstree_from_file(fstree_t *fs, const char *filename, const char *basepath) +{ + istream_t *fp; + int ret; + + fp = istream_open_file(filename); + if (fp == NULL) + return -1; + + ret = fstree_from_file_stream(fs, fp, basepath); + + sqfs_drop(fp); + return ret; +} diff --git a/bin/gensquashfs/src/mkfs.c b/bin/gensquashfs/src/mkfs.c new file mode 100644 index 0000000..c773dd7 --- /dev/null +++ b/bin/gensquashfs/src/mkfs.c @@ -0,0 +1,215 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * mkfs.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "mkfs.h" + +static int pack_files(sqfs_block_processor_t *data, fstree_t *fs, + options_t *opt) +{ + sqfs_u64 filesize; + sqfs_file_t *file; + tree_node_t *node; + const char *path; + char *node_path; + file_info_t *fi; + int flags; + int ret; + + if (opt->packdir != NULL && chdir(opt->packdir) != 0) { + perror(opt->packdir); + return -1; + } + + for (fi = fs->files; fi != NULL; fi = fi->next) { + if (fi->input_file == NULL) { + node = container_of(fi, tree_node_t, data.file); + + node_path = fstree_get_path(node); + if (node_path == NULL) { + perror("reconstructing file path"); + return -1; + } + + ret = canonicalize_name(node_path); + assert(ret == 0); + + path = node_path; + } else { + node_path = NULL; + path = fi->input_file; + } + + if (!opt->cfg.quiet) + printf("packing %s\n", path); + + file = sqfs_open_file(path, SQFS_FILE_OPEN_READ_ONLY); + if (file == NULL) { + perror(path); + free(node_path); + return -1; + } + + flags = fi->flags; + filesize = file->get_size(file); + + if (opt->no_tail_packing && filesize > opt->cfg.block_size) + flags |= SQFS_BLK_DONT_FRAGMENT; + + ret = write_data_from_file(path, data, &fi->inode, file, flags); + sqfs_drop(file); + free(node_path); + + if (ret) + return -1; + } + + return 0; +} + +static int relabel_tree_dfs(const char *filename, sqfs_xattr_writer_t *xwr, + tree_node_t *n, void *selinux_handle) +{ + char *path = fstree_get_path(n); + int ret; + + if (path == NULL) { + perror("getting absolute node path for SELinux relabeling"); + return -1; + } + + ret = sqfs_xattr_writer_begin(xwr, 0); + if (ret) { + sqfs_perror(filename, "recording xattr key-value pairs", ret); + return -1; + } + + if (selinux_relable_node(selinux_handle, xwr, n, path)) { + free(path); + return -1; + } + + ret = sqfs_xattr_writer_end(xwr, &n->xattr_idx); + if (ret) { + sqfs_perror(filename, "flushing completed key-value pairs", + ret); + return -1; + } + + free(path); + + if (S_ISDIR(n->mode)) { + for (n = n->data.dir.children; n != NULL; n = n->next) { + if (relabel_tree_dfs(filename, xwr, n, selinux_handle)) + return -1; + } + } + + return 0; +} + +static int read_fstree(fstree_t *fs, options_t *opt, sqfs_xattr_writer_t *xwr, + void *selinux_handle) +{ + int ret; + + ret = fstree_from_file(fs, opt->infile, opt->packdir); + + if (ret == 0 && selinux_handle != NULL) + ret = relabel_tree_dfs(opt->cfg.filename, xwr, + fs->root, selinux_handle); + + return ret; +} + +static void override_owner_dfs(const options_t *opt, tree_node_t *n) +{ + if (opt->force_uid) + n->uid = opt->force_uid_value; + + if (opt->force_gid) + n->gid = opt->force_gid_value; + + if (S_ISDIR(n->mode)) { + for (n = n->data.dir.children; n != NULL; n = n->next) + override_owner_dfs(opt, n); + } +} + +int main(int argc, char **argv) +{ + int status = EXIT_FAILURE; + istream_t *sortfile = NULL; + void *sehnd = NULL; + void *xattrmap = NULL; + sqfs_writer_t sqfs; + options_t opt; + + process_command_line(&opt, argc, argv); + + if (sqfs_writer_init(&sqfs, &opt.cfg)) + return EXIT_FAILURE; + + if (opt.selinux != NULL) { + sehnd = selinux_open_context_file(opt.selinux); + if (sehnd == NULL) + goto out; + } + if (opt.xattr_file != NULL) { + xattrmap = xattr_open_map_file(opt.xattr_file); + if (xattrmap == NULL) + goto out; + } + + if (opt.sortfile != NULL) { + sortfile = istream_open_file(opt.sortfile); + if (sortfile == NULL) + goto out; + } + + if (opt.infile == NULL) { + if (fstree_from_dir(&sqfs.fs, sqfs.fs.root, opt.packdir, + NULL, NULL, opt.dirscan_flags)) { + goto out; + } + } else { + if (read_fstree(&sqfs.fs, &opt, sqfs.xwr, sehnd)) + goto out; + } + + if (opt.force_uid || opt.force_gid) + override_owner_dfs(&opt, sqfs.fs.root); + + if (fstree_post_process(&sqfs.fs)) + goto out; + + if (opt.infile == NULL) { + if (xattrs_from_dir(&sqfs.fs, opt.packdir, sehnd, xattrmap, + sqfs.xwr, opt.scan_xattr)) { + goto out; + } + } + + if (sortfile != NULL) { + if (fstree_sort_files(&sqfs.fs, sortfile)) + goto out; + } + + if (pack_files(sqfs.data, &sqfs.fs, &opt)) + goto out; + + if (sqfs_writer_finish(&sqfs, &opt.cfg)) + goto out; + + status = EXIT_SUCCESS; +out: + sqfs_writer_cleanup(&sqfs, status); + if (sehnd != NULL) + selinux_close_context_file(sehnd); + if (sortfile != NULL) + sqfs_drop(sortfile); + free(opt.packdir); + return status; +} diff --git a/bin/gensquashfs/src/mkfs.h b/bin/gensquashfs/src/mkfs.h new file mode 100644 index 0000000..53fb018 --- /dev/null +++ b/bin/gensquashfs/src/mkfs.h @@ -0,0 +1,137 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * mkfs.h + * + * Copyright (C) 2019 David Oberhollenzer + * Copyright (C) 2022 Enno Boland + */ +#ifndef MKFS_H +#define MKFS_H + +#include "config.h" + +#include "common.h" +#include "fstree.h" +#include "util/util.h" +#include "io/file.h" + +#ifdef HAVE_SYS_XATTR_H +#include + +#if defined(__APPLE__) && defined(__MACH__) +#define llistxattr(path, list, size) \ + listxattr(path, list, size, XATTR_NOFOLLOW) + +#define lgetxattr(path, name, value, size) \ + getxattr(path, name, value, size, 0, XATTR_NOFOLLOW) +#endif +#endif + +#ifdef WITH_SELINUX +#include +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +typedef struct { + sqfs_writer_cfg_t cfg; + unsigned int dirscan_flags; + const char *infile; + const char *selinux; + const char *xattr_file; + const char *sortfile; + bool no_tail_packing; + + /* copied from command line or constructed from infile argument + if not specified. Must be free'd. */ + char *packdir; + + unsigned int force_uid_value; + unsigned int force_gid_value; + bool force_uid; + bool force_gid; + + bool scan_xattr; +} options_t; + +struct XattrMapEntry { + char *key; + sqfs_u8 *value; + size_t value_len; + struct XattrMapEntry *next; +}; + +struct XattrMapPattern { + char *path; + struct XattrMapEntry *entries; + struct XattrMapPattern *next; +}; + +struct XattrMap { + struct XattrMapPattern *patterns; +}; + +void process_command_line(options_t *opt, int argc, char **argv); + +int xattrs_from_dir(fstree_t *fs, const char *path, void *selinux_handle, + void *xattr_map, sqfs_xattr_writer_t *xwr, bool scan_xattr); + +void *xattr_open_map_file(const char *path); + +int +xattr_apply_map_file(char *path, void *map, sqfs_xattr_writer_t *xwr); + +void xattr_close_map_file(void *xattr_map); + +void *selinux_open_context_file(const char *filename); + +int selinux_relable_node(void *sehnd, sqfs_xattr_writer_t *xwr, + tree_node_t *node, const char *path); + +void selinux_close_context_file(void *sehnd); + +/* + Parses the file format accepted by gensquashfs and produce a file system + tree from it. File input paths are interpreted as relative to the current + working directory. + + On failure, an error report with filename and line number is written + to stderr. + + Returns 0 on success. + */ +int fstree_from_file(fstree_t *fs, const char *filename, + const char *basepath); + +int fstree_from_file_stream(fstree_t *fs, istream_t *file, + const char *basepath); + +/* + Recursively scan a directory to build a file system tree. + + Returns 0 on success, prints to stderr on failure. + */ +int fstree_from_dir(fstree_t *fs, tree_node_t *root, + const char *path, scan_node_callback cb, void *user, + unsigned int flags); + +/* + Same as fstree_from_dir, but scans a sub-directory inside the specified path. + + Returns 0 on success, prints to stderr on failure. + */ +int fstree_from_subdir(fstree_t *fs, tree_node_t *root, + const char *path, const char *subdir, + scan_node_callback cb, void *user, unsigned int flags); + +int fstree_sort_files(fstree_t *fs, istream_t *sortfile); + +#endif /* MKFS_H */ diff --git a/bin/gensquashfs/src/options.c b/bin/gensquashfs/src/options.c new file mode 100644 index 0000000..f263bce --- /dev/null +++ b/bin/gensquashfs/src/options.c @@ -0,0 +1,383 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * options.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "mkfs.h" + +enum { + ALL_ROOT_OPTION = 1, +}; + +static struct option long_opts[] = { + { "all-root", no_argument, NULL, ALL_ROOT_OPTION }, + { "set-uid", required_argument, NULL, 'u' }, + { "set-gid", required_argument, NULL, 'g' }, + { "compressor", required_argument, NULL, 'c' }, + { "block-size", required_argument, NULL, 'b' }, + { "dev-block-size", required_argument, NULL, 'B' }, + { "defaults", required_argument, NULL, 'd' }, + { "comp-extra", required_argument, NULL, 'X' }, + { "pack-file", required_argument, NULL, 'F' }, + { "pack-dir", required_argument, NULL, 'D' }, + { "num-jobs", required_argument, NULL, 'j' }, + { "queue-backlog", required_argument, NULL, 'Q' }, + { "keep-time", no_argument, NULL, 'k' }, +#ifdef HAVE_SYS_XATTR_H + { "keep-xattr", no_argument, NULL, 'x' }, +#endif + { "one-file-system", no_argument, NULL, 'o' }, + { "exportable", no_argument, NULL, 'e' }, + { "no-tail-packing", no_argument, NULL, 'T' }, + { "force", no_argument, NULL, 'f' }, + { "quiet", no_argument, NULL, 'q' }, +#ifdef WITH_SELINUX + { "selinux", required_argument, NULL, 's' }, +#endif + { "xattr-file", required_argument, NULL, 'A' }, + { "sort-file", required_argument, NULL, 'S' }, + { "version", no_argument, NULL, 'V' }, + { "help", no_argument, NULL, 'h' }, + { NULL, 0, NULL, 0 }, +}; + +static const char *short_opts = "F:D:X:c:b:B:d:u:g:j:Q:S:A:kxoefqThV" +#ifdef WITH_SELINUX +"s:" +#endif +#ifdef HAVE_SYS_XATTR_H +"x" +#endif +; + +static const char *help_string = +"Usage: gensquashfs [OPTIONS...] \n" +"\n" +"Possible options:\n" +"\n"; + +static const char *pack_options = +" --pack-file, -F Use a `gen_init_cpio` style description file.\n" +" The file format is specified below.\n" +" If --pack-dir is used, input file paths are\n" +" relative to the pack directory, otherwise\n" +" they are relative to the directory the pack\n" +" file is in.\n" +" --pack-dir, -D If --pack-file is used, this is the root path\n" +" relative to which to read files. If no pack\n" +" file is specified, pack the contents of the\n" +" given directory. The directory becomes the\n" +" file system root.\n" +"\n" +" --compressor, -c Select the compressor to use.\n" +" A list of available compressors is below.\n" +" --comp-extra, -X A comma separated list of extra options for\n" +" the selected compressor. Specify 'help' to\n" +" get a list of available options.\n" +" --num-jobs, -j Number of compressor jobs to create.\n" +" --queue-backlog, -Q Maximum number of data blocks in the thread\n" +" worker queue before the packer starts waiting\n" +" for the block processors to catch up.\n" +" Defaults to 10 times the number of jobs.\n" +" --block-size, -b Block size to use for Squashfs image.\n" +" Defaults to %u.\n" +" --dev-block-size, -B Device block size to padd the image to.\n" +" Defaults to %u.\n" +" --defaults, -d A comma separated list of default values for\n" +" implicitly created directories.\n" +"\n" +" Possible options:\n" +" uid= 0 if not set.\n" +" gid= 0 if not set.\n" +" mode= 0755 if not set.\n" +" mtime= 0 if not set.\n" +"\n" +" --set-uid, -u Force the owners user ID for ALL inodes to\n" +" this value, no matter what the pack file or\n" +" directory entries actually specify.\n" +" --set-gid, -g Force the owners group ID for ALL inodes to\n" +" this value, no matter what the pack file or\n" +" directory entries actually specify.\n" +" --all-root A short hand for `--set-uid 0 --set-gid 0`.\n" +"\n"; + +const char *extra_options = +" --sort-file, -S Specify a \"sort file\" that can be used to\n" +" micro manage the order of files during packing\n" +" and behaviour (compression, fragmentation, ..)\n" +"\n" +#ifdef WITH_SELINUX +" --selinux, -s Specify an SELinux label file to get context\n" +" attributes from.\n" +#endif +" --xattr-file, -A Specify an Xattr file to get extended attributes\n" +" for loading xattrs\n" +" --keep-time, -k When using --pack-dir only, use the timestamps\n" +" from the input files instead of setting\n" +" defaults on all input paths.\n" +" --keep-xattr, -x When using --pack-dir only, read and pack the\n" +" extended attributes from the input files.\n" +" --one-file-system, -o When using --pack-dir only, stay in local file\n" +" system and do not cross mount points.\n" +" --exportable, -e Generate an export table for NFS support.\n" +" --no-tail-packing, -T Do not perform tail end packing on files that\n" +" are larger than block size.\n" +" --force, -f Overwrite the output file if it exists.\n" +" --quiet, -q Do not print out progress reports.\n" +" --help, -h Print help text and exit.\n" +" --version, -V Print version information and exit.\n" +"\n"; + +const char *pack_details = +"Example of a pack file:\n" +"\n" +" # A simple squashfs image\n" +" dir /dev 0755 0 0\n" +" nod /dev/console 0600 0 0 c 5 1\n" +" dir /root 0700 0 0\n" +" \n" +" # `slink` for symlink, `link` for hard links\n" +" slink /lib 0777 0 0 /usr/lib\n" +" link /init 0777 0 0 /sbin/init\n" +" \n" +" # Add a file. Input is relative to listing or pack dir.\n" +" file /sbin/init 0755 0 0 ../init/sbin/init\n" +" \n" +" # Read bin/bash, relative to listing or pack dir.\n" +" # Implicitly create /bin.\n" +" file /bin/bash 0755 0 0\n" +" \n" +" # file name with a space in it.\n" +" file \"/opt/my app/\\\"special\\\"/data\" 0600 0 0\n" +" \n" +" # collect the contents of ./lib and put it under /usr/lib\n" +" glob /usr/lib 0755 0 0 -type d ./lib\n" +" glob /usr/lib 0755 0 0 -type f -name \"*.so.*\" ./lib\n" +" glob /usr/lib 0777 0 0 -type l -name \"*.so.*\" ./lib\n" +"\n\n"; + +const char *sort_details = +"When using a sort file, the specified paths are within the SquashFS image.\n" +"Files with lower priority are packed first, default priority is 0.\n" +"The sorting is stable, files with the same priority do not change place\n" +"relative to each other.\n" +"\n" +"Example:\n" +" # Specify a packing order with file globbing\n" +" -8000 [glob] bin/*\n" +" -5000 [glob] lib/*\n" +"\n" +" # glob_no_path means * is allowed to match /\n" +" -1000 [glob_no_path] share/*\n" +"\n" +" # Our boot loader needs this\n" +" -100000 [dont_compress,dont_fragment,nosparse] boot/vmlinuz\n" +"\n" +" # For demonstration, a quoted filename and no flags\n" +" 1337 \"usr/share/my \\\"special\\\" file \"\n" +"\n\n"; + +static const char *xattr_details = +"The format of xattr files tries to immitate the format generated\n" +"by `getfattr --dump`.\n" +"\n" +"Example:\n" +" # file: dev/\n" +" security.selinux=\"system_u:object_r:device_t:s0\"\n" +" user.beverage_preference=0xCAFECAFEDECAFBAD\n" +"\n" +" # file: dev/rfkill\n" +" security.selinux=\"system_u:object_r:wireless_device_t:s0\"\n" +" system.posix_acl_access=0sSGVsbG8gdGhlcmUgOi0pCg==\n" +"\n\n"; + +void process_command_line(options_t *opt, int argc, char **argv) +{ + bool have_compressor; + int i, ret; + + memset(opt, 0, sizeof(*opt)); + sqfs_writer_cfg_init(&opt->cfg); + + for (;;) { + i = getopt_long(argc, argv, short_opts, long_opts, NULL); + if (i == -1) + break; + + switch (i) { + case ALL_ROOT_OPTION: + opt->force_uid_value = 0; + opt->force_gid_value = 0; + opt->force_uid = true; + opt->force_gid = true; + break; + case 'u': + opt->force_uid_value = strtol(optarg, NULL, 0); + opt->force_uid = true; + break; + case 'g': + opt->force_gid_value = strtol(optarg, NULL, 0); + opt->force_gid = true; + break; + case 'T': + opt->no_tail_packing = true; + break; + case 'c': + have_compressor = true; + ret = sqfs_compressor_id_from_name(optarg); + + if (ret < 0) { + have_compressor = false; +#ifdef WITH_LZO + if (opt->cfg.comp_id == SQFS_COMP_LZO) + have_compressor = true; +#endif + } + + if (!have_compressor) { + fprintf(stderr, "Unsupported compressor '%s'\n", + optarg); + exit(EXIT_FAILURE); + } + + opt->cfg.comp_id = ret; + break; + case 'b': + if (parse_size("Block size", &opt->cfg.block_size, + optarg, 0)) { + exit(EXIT_FAILURE); + } + break; + case 'j': + opt->cfg.num_jobs = strtol(optarg, NULL, 0); + break; + case 'Q': + opt->cfg.max_backlog = strtol(optarg, NULL, 0); + break; + case 'B': + if (parse_size("Device block size", + &opt->cfg.devblksize, optarg, 0)) { + exit(EXIT_FAILURE); + } + if (opt->cfg.devblksize < 1024) { + fputs("Device block size must be at " + "least 1024\n", stderr); + exit(EXIT_FAILURE); + } + break; + case 'd': + opt->cfg.fs_defaults = optarg; + break; + case 'k': + opt->dirscan_flags |= DIR_SCAN_KEEP_TIME; + break; +#ifdef HAVE_SYS_XATTR_H + case 'x': + opt->scan_xattr = true; + break; +#endif + case 'o': + opt->dirscan_flags |= DIR_SCAN_ONE_FILESYSTEM; + break; + case 'e': + opt->cfg.exportable = true; + break; + case 'f': + opt->cfg.outmode |= SQFS_FILE_OPEN_OVERWRITE; + break; + case 'q': + opt->cfg.quiet = true; + break; + case 'X': + opt->cfg.comp_extra = optarg; + break; + case 'F': + opt->infile = optarg; + break; + case 'D': + free(opt->packdir); + opt->packdir = strdup(optarg); + if (opt->packdir == NULL) { + perror(optarg); + exit(EXIT_FAILURE); + } + break; +#ifdef WITH_SELINUX + case 's': + opt->selinux = optarg; + break; +#endif + case 'A': + opt->xattr_file = optarg; + break; + case 'S': + opt->sortfile = optarg; + break; + case 'h': + fputs(help_string, stdout); + printf(pack_options, SQFS_DEFAULT_BLOCK_SIZE, + SQFS_DEVBLK_SIZE); + fputs(extra_options, stdout); + fputs(pack_details, stdout); + fputs(sort_details, stdout); + fputs(xattr_details, stdout); + compressor_print_available(); + exit(EXIT_SUCCESS); + case 'V': + print_version("gensquashfs"); + exit(EXIT_SUCCESS); + default: + goto fail_arg; + } + } + + if (opt->cfg.num_jobs < 1) + opt->cfg.num_jobs = 1; + + if (opt->cfg.max_backlog < 1) + opt->cfg.max_backlog = 10 * opt->cfg.num_jobs; + + if (opt->cfg.comp_extra != NULL && + strcmp(opt->cfg.comp_extra, "help") == 0) { + compressor_print_help(opt->cfg.comp_id); + exit(EXIT_SUCCESS); + } + + if (opt->infile == NULL && opt->packdir == NULL) { + fputs("No input file or directory specified.\n", stderr); + goto fail_arg; + } + + if (optind >= argc) { + fputs("No output file specified.\n", stderr); + goto fail_arg; + } + + opt->cfg.filename = argv[optind++]; + + if (optind < argc) { + fputs("Unknown extra arguments specified.\n", stderr); + goto fail_arg; + } + + /* construct packdir if not specified */ + if (opt->packdir == NULL && opt->infile != NULL) { + const char *split = strrchr(opt->infile, '/'); + + if (split != NULL) { + opt->packdir = strndup(opt->infile, + split - opt->infile); + + if (opt->packdir == NULL) { + perror("constructing input directory path"); + exit(EXIT_FAILURE); + } + } + } + return; +fail_arg: + fputs("Try `gensquashfs --help' for more information.\n", stderr); + free(opt->packdir); + exit(EXIT_FAILURE); +} diff --git a/bin/gensquashfs/src/selinux.c b/bin/gensquashfs/src/selinux.c new file mode 100644 index 0000000..678723b --- /dev/null +++ b/bin/gensquashfs/src/selinux.c @@ -0,0 +1,78 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * selinux.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "mkfs.h" + +#define XATTR_NAME_SELINUX "security.selinux" +#define XATTR_VALUE_SELINUX "system_u:object_r:unlabeled_t:s0" + +#ifdef WITH_SELINUX +int selinux_relable_node(void *sehnd, sqfs_xattr_writer_t *xwr, + tree_node_t *node, const char *path) +{ + char *context = NULL; + int ret; + + if (selabel_lookup(sehnd, &context, path, node->mode) < 0) { + context = strdup(XATTR_VALUE_SELINUX); + if (context == NULL) + goto fail; + } + + ret = sqfs_xattr_writer_add(xwr, XATTR_NAME_SELINUX, + context, strlen(context)); + free(context); + + if (ret) + sqfs_perror(node->name, "storing SELinux xattr", ret); + + return ret; +fail: + perror("relabeling files"); + return -1; +} + +void *selinux_open_context_file(const char *filename) +{ + struct selabel_handle *sehnd; + struct selinux_opt seopts[] = { + { SELABEL_OPT_PATH, filename }, + }; + + sehnd = selabel_open(SELABEL_CTX_FILE, seopts, 1); + if (sehnd == NULL) + perror(filename); + + return sehnd; +} + +void selinux_close_context_file(void *sehnd) +{ + selabel_close(sehnd); +} +#else +int selinux_relable_node(void *sehnd, sqfs_xattr_writer_t *xwr, + tree_node_t *node, const char *path) +{ + (void)sehnd; (void)xwr; (void)node; (void)path; + fputs("Built without SELinux support, cannot add SELinux labels\n", + stderr); + return -1; +} + +void *selinux_open_context_file(const char *filename) +{ + (void)filename; + fputs("Built without SELinux support, cannot open contexts file\n", + stderr); + return NULL; +} + +void selinux_close_context_file(void *sehnd) +{ + (void)sehnd; +} +#endif diff --git a/bin/gensquashfs/src/sort_by_file.c b/bin/gensquashfs/src/sort_by_file.c new file mode 100644 index 0000000..a555718 --- /dev/null +++ b/bin/gensquashfs/src/sort_by_file.c @@ -0,0 +1,368 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * sort_by_file.c + * + * Copyright (C) 2021 David Oberhollenzer + */ +#include "config.h" + +#include "util/util.h" +#include "fstree.h" +#include "mkfs.h" + +#include "sqfs/block.h" + +#include +#include +#include + +static int decode_priority(const char *filename, size_t line_no, + char *line, sqfs_s64 *priority) +{ + bool negative = false; + size_t i = 0; + + if (line[0] == '-') { + negative = true; + i = 1; + } + + if (!isdigit(line[i])) + goto fail_number; + + *priority = 0; + + for (; isdigit(line[i]); ++i) { + sqfs_s64 x = line[i] - '0'; + + if ((*priority) >= ((0x7FFFFFFFFFFFFFFFL - x) / 10L)) + goto fail_ov; + + (*priority) = (*priority) * 10 + x; + } + + if (!isspace(line[i])) + goto fail_filename; + + while (isspace(line[i])) + ++i; + + if (line[i] == '\0') + goto fail_filename; + + if (negative) + (*priority) = -(*priority); + + memmove(line, line + i, strlen(line + i) + 1); + return 0; +fail_number: + fprintf(stderr, "%s: " PRI_SZ ": Line must start with " + "numeric sort priority.\n", + filename, line_no); + return -1; +fail_ov: + fprintf(stderr, "%s: " PRI_SZ ": Numeric overflow in sort priority.\n", + filename, line_no); + return -1; +fail_filename: + fprintf(stderr, "%s: " PRI_SZ ": Expacted ` ` " + "after sort priority.\n", + filename, line_no); + return -1; +} + +static int decode_filename(const char *filename, size_t line_no, char *buffer) +{ + char *src, *dst; + + if (buffer[0] == '"') { + src = buffer + 1; + dst = buffer; + + for (;;) { + if (src[0] == '\0') + goto fail_match; + + if (src[0] == '"') { + ++src; + break; + } + + if (src[0] == '\\') { + switch (src[1]) { + case '\\': + *(dst++) = '\\'; + src += 2; + break; + case '"': + *(dst++) = '"'; + src += 2; + break; + default: + goto fail_escape; + } + } else { + *(dst++) = *(src++); + } + } + + if (*src != '\0') + return -1; + } + + if (canonicalize_name(buffer)) + goto fail_canon; + return 0; +fail_canon: + fprintf(stderr, "%s: " PRI_SZ ": Malformed filename.\n", + filename, line_no); + return -1; +fail_escape: + fprintf(stderr, "%s: " PRI_SZ ": Unknown escape sequence `\\%c` " + "in filename.\n", filename, line_no, src[1]); + return -1; +fail_match: + fprintf(stderr, "%s: " PRI_SZ ": Unmatched '\"' in filename.\n", + filename, line_no); + return -1; +} + +static int decode_flags(const char *filename, size_t line_no, bool *do_glob, + bool *path_glob, int *flags, char *line) +{ + char *start = line; + + *do_glob = false; + *path_glob = false; + *flags = 0; + + if (*(line++) != '[') + return 0; + + for (;;) { + while (isspace(*line)) + ++line; + + if (*line == ']') { + ++line; + break; + } + + if (strncmp(line, "glob_no_path", 12) == 0) { + line += 12; + *do_glob = true; + *path_glob = false; + } else if (strncmp(line, "glob", 4) == 0) { + line += 4; + *do_glob = true; + *path_glob = true; + } else if (strncmp(line, "dont_fragment", 13) == 0) { + line += 13; + (*flags) |= SQFS_BLK_DONT_FRAGMENT; + } else if (strncmp(line, "align", 5) == 0) { + line += 5; + (*flags) |= SQFS_BLK_ALIGN; + } else if (strncmp(line, "dont_compress", 13) == 0) { + line += 13; + (*flags) |= SQFS_BLK_DONT_COMPRESS; + } else if (strncmp(line, "dont_deduplicate", 16) == 0) { + line += 16; + (*flags) |= SQFS_BLK_DONT_DEDUPLICATE; + } else if (strncmp(line, "nosparse", 8) == 0) { + line += 8; + (*flags) |= SQFS_BLK_IGNORE_SPARSE; + } else { + goto fail_flag; + } + + while (isspace(*line)) + ++line; + + if (*line == ']') { + ++line; + break; + } + + if (*(line++) != ',') + goto fail_sep; + } + + if (!isspace(*line)) + goto fail_fname; + + while (isspace(*line)) + ++line; + + memmove(start, line, strlen(line) + 1); + return 0; +fail_fname: + fprintf(stderr, "%s: " PRI_SZ ": Expected ` ` " + "after flag list.\n", filename, line_no); + return -1; +fail_sep: + fprintf(stderr, "%s: " PRI_SZ ": Unexpected '%c' after flag.\n", + filename, line_no, *line); + return -1; +fail_flag: + fprintf(stderr, "%s: " PRI_SZ ": Unknown flag `%.3s...`.\n", + filename, line_no, line); + return -1; +} + +static void sort_file_list(fstree_t *fs) +{ + file_info_t *out = NULL, *out_last = NULL; + + while (fs->files != NULL) { + sqfs_s64 lowest = fs->files->priority; + file_info_t *it, *prev; + + for (it = fs->files; it != NULL; it = it->next) { + if (it->priority < lowest) + lowest = it->priority; + } + + it = fs->files; + prev = NULL; + + while (it != NULL) { + if (it->priority != lowest) { + prev = it; + it = it->next; + continue; + } + + if (prev == NULL) { + fs->files = it->next; + } else { + prev->next = it->next; + } + + if (out == NULL) { + out = it; + } else { + out_last->next = it; + } + + out_last = it; + it = it->next; + out_last->next = NULL; + } + } + + fs->files = out; +} + +int fstree_sort_files(fstree_t *fs, istream_t *sortfile) +{ + const char *filename; + size_t line_num = 1; + file_info_t *it; + + for (it = fs->files; it != NULL; it = it->next) { + it->priority = 0; + it->flags = 0; + it->already_matched = false; + } + + filename = istream_get_filename(sortfile); + + for (;;) { + bool do_glob, path_glob, have_match; + char *line = NULL; + sqfs_s64 priority; + int ret, flags; + + ret = istream_get_line(sortfile, &line, &line_num, + ISTREAM_LINE_LTRIM | + ISTREAM_LINE_RTRIM | + ISTREAM_LINE_SKIP_EMPTY); + if (ret != 0) { + free(line); + if (ret < 0) + return -1; + break; + } + + if (line[0] == '#') { + free(line); + continue; + } + + if (decode_priority(filename, line_num, line, &priority)) { + free(line); + return -1; + } + + if (decode_flags(filename, line_num, &do_glob, &path_glob, + &flags, line)) { + free(line); + return -1; + } + + if (decode_filename(filename, line_num, line)) { + free(line); + return -1; + } + + have_match = false; + + for (it = fs->files; it != NULL; it = it->next) { + tree_node_t *node; + char *path; + + if (it->already_matched) + continue; + + node = container_of(it, tree_node_t, data.file); + path = fstree_get_path(node); + if (path == NULL) { + fprintf(stderr, "%s: " PRI_SZ ": out-of-memory\n", + filename, line_num); + free(line); + return -1; + } + + if (canonicalize_name(path)) { + fprintf(stderr, + "%s: " PRI_SZ ": [BUG] error " + "reconstructing node path\n", + filename, line_num); + free(line); + free(path); + return -1; + } + + if (do_glob) { + ret = fnmatch(line, path, + path_glob ? FNM_PATHNAME : 0); + + } else { + ret = strcmp(path, line); + } + + free(path); + + if (ret == 0) { + have_match = true; + it->flags = flags; + it->priority = priority; + it->already_matched = true; + + if (!do_glob) + break; + } + } + + if (!have_match) { + fprintf(stderr, "WARNING: %s: " PRI_SZ ": no match " + "for '%s'.\n", + filename, line_num, line); + } + + free(line); + } + + sort_file_list(fs); + return 0; +} diff --git a/bin/rdsquashfs/Makemodule.am b/bin/rdsquashfs/Makemodule.am index 1ff9c60..f8f9d3d 100644 --- a/bin/rdsquashfs/Makemodule.am +++ b/bin/rdsquashfs/Makemodule.am @@ -1,8 +1,8 @@ -rdsquashfs_SOURCES = bin/rdsquashfs/rdsquashfs.c bin/rdsquashfs/rdsquashfs.h -rdsquashfs_SOURCES += bin/rdsquashfs/list_files.c bin/rdsquashfs/options.c -rdsquashfs_SOURCES += bin/rdsquashfs/restore_fstree.c bin/rdsquashfs/describe.c -rdsquashfs_SOURCES += bin/rdsquashfs/fill_files.c bin/rdsquashfs/dump_xattrs.c -rdsquashfs_SOURCES += bin/rdsquashfs/stat.c +rdsquashfs_SOURCES = bin/rdsquashfs/src/rdsquashfs.c \ + bin/rdsquashfs/src/rdsquashfs.h bin/rdsquashfs/src/list_files.c \ + bin/rdsquashfs/src/options.c bin/rdsquashfs/src/restore_fstree.c \ + bin/rdsquashfs/src/describe.c bin/rdsquashfs/src/fill_files.c \ + bin/rdsquashfs/src/dump_xattrs.c bin/rdsquashfs/src/stat.c rdsquashfs_CFLAGS = $(AM_CFLAGS) $(PTHREAD_CFLAGS) rdsquashfs_LDADD = libcommon.a libio.a libcompat.a libutil.a libsquashfs.la rdsquashfs_LDADD += libfstree.a $(LZO_LIBS) $(PTHREAD_LIBS) diff --git a/bin/rdsquashfs/describe.c b/bin/rdsquashfs/describe.c deleted file mode 100644 index 540b126..0000000 --- a/bin/rdsquashfs/describe.c +++ /dev/null @@ -1,139 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * describe.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "rdsquashfs.h" - -static int print_name(const sqfs_tree_node_t *n, bool dont_escape) -{ - char *start, *ptr, *name; - int ret; - - ret = sqfs_tree_node_get_path(n, &name); - if (ret != 0) { - sqfs_perror(NULL, "Recovering file path of tree node", ret); - return -1; - } - - if (canonicalize_name(name) != 0) { - fprintf(stderr, "Error sanitizing file path '%s'\n", name); - sqfs_free(name); - return -1; - } - - if (dont_escape || (strchr(name, ' ') == NULL && - strchr(name, '"') == NULL)) { - fputs(name, stdout); - } else { - fputc('"', stdout); - - ptr = strchr(name, '"'); - - if (ptr != NULL) { - start = name; - - do { - fwrite(start, 1, ptr - start, stdout); - fputs("\\\"", stdout); - start = ptr + 1; - ptr = strchr(start, '"'); - } while (ptr != NULL); - - fputs(start, stdout); - } else { - fputs(name, stdout); - } - - fputc('"', stdout); - } - - sqfs_free(name); - return 0; -} - -static void print_perm(const sqfs_tree_node_t *n) -{ - printf(" 0%o %u %u", (unsigned int)n->inode->base.mode & (~S_IFMT), - n->uid, n->gid); -} - -static int print_simple(const char *type, const sqfs_tree_node_t *n, - const char *extra) -{ - printf("%s ", type); - if (print_name(n, false)) - return -1; - print_perm(n); - if (extra != NULL) - printf(" %s", extra); - fputc('\n', stdout); - return 0; -} - -int describe_tree(const sqfs_tree_node_t *root, const char *unpack_root) -{ - const sqfs_tree_node_t *n; - - if (!is_filename_sane((const char *)root->name, false)) { - fprintf(stderr, "Encountered illegal file name '%s'\n", - root->name); - return -1; - } - - switch (root->inode->base.mode & S_IFMT) { - case S_IFSOCK: - return print_simple("sock", root, NULL); - case S_IFLNK: - return print_simple("slink", root, - (const char *)root->inode->extra); - case S_IFIFO: - return print_simple("pipe", root, NULL); - case S_IFREG: - if (unpack_root == NULL) - return print_simple("file", root, NULL); - - fputs("file ", stdout); - if (print_name(root, false)) - return -1; - print_perm(root); - printf(" %s/", unpack_root); - if (print_name(root, true)) - return -1; - fputc('\n', stdout); - break; - case S_IFCHR: - case S_IFBLK: { - char buffer[32]; - sqfs_u32 devno; - - if (root->inode->base.type == SQFS_INODE_EXT_BDEV || - root->inode->base.type == SQFS_INODE_EXT_CDEV) { - devno = root->inode->data.dev_ext.devno; - } else { - devno = root->inode->data.dev.devno; - } - - sprintf(buffer, "%c %u %u", - S_ISCHR(root->inode->base.mode) ? 'c' : 'b', - major(devno), minor(devno)); - return print_simple("nod", root, buffer); - } - case S_IFDIR: - if (root->name[0] != '\0') { - if (print_simple("dir", root, NULL)) - return -1; - } - - for (n = root->children; n != NULL; n = n->next) { - if (describe_tree(n, unpack_root)) - return -1; - } - break; - default: - break; - } - - return 0; -} diff --git a/bin/rdsquashfs/dump_xattrs.c b/bin/rdsquashfs/dump_xattrs.c deleted file mode 100644 index 9dbe437..0000000 --- a/bin/rdsquashfs/dump_xattrs.c +++ /dev/null @@ -1,120 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * dump_xattrs.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "rdsquashfs.h" - -static void print_hex(const sqfs_u8 *value, size_t len) -{ - printf("0x"); - - while (len--) - printf("%02X", *(value++)); -} - -static bool is_printable(const sqfs_u8 *value, size_t len) -{ - size_t utf8_cont = 0; - sqfs_u8 x; - - while (len--) { - x = *(value++); - - if (utf8_cont > 0) { - if ((x & 0xC0) != 0x80) - return false; - - --utf8_cont; - } else { - if (x < 0x80) { - if (x < 0x20) { - if (x >= 0x07 && x <= 0x0D) - continue; - if (x == 0x00) - continue; - return false; - } - - if (x == 0x7F) - return false; - } - - if ((x & 0xE0) == 0xC0) { - utf8_cont = 1; - } else if ((x & 0xF0) == 0xE0) { - utf8_cont = 2; - } else if ((x & 0xF8) == 0xF0) { - utf8_cont = 3; - } else if ((x & 0xFC) == 0xF8) { - utf8_cont = 4; - } else if ((x & 0xFE) == 0xFC) { - utf8_cont = 5; - } - - if (utf8_cont > 0 && len < utf8_cont) - return false; - } - } - - return true; -} - -int dump_xattrs(sqfs_xattr_reader_t *xattr, const sqfs_inode_generic_t *inode) -{ - sqfs_xattr_value_t *value; - sqfs_xattr_entry_t *key; - sqfs_xattr_id_t desc; - sqfs_u32 index; - size_t i; - - if (xattr == NULL) - return 0; - - sqfs_inode_get_xattr_index(inode, &index); - - if (index == 0xFFFFFFFF) - return 0; - - if (sqfs_xattr_reader_get_desc(xattr, index, &desc)) { - fputs("Error resolving xattr index\n", stderr); - return -1; - } - - if (sqfs_xattr_reader_seek_kv(xattr, &desc)) { - fputs("Error locating xattr key-value pairs\n", stderr); - return -1; - } - - for (i = 0; i < desc.count; ++i) { - if (sqfs_xattr_reader_read_key(xattr, &key)) { - fputs("Error reading xattr key\n", stderr); - return -1; - } - - if (sqfs_xattr_reader_read_value(xattr, key, &value)) { - fputs("Error reading xattr value\n", stderr); - sqfs_free(key); - return -1; - } - - if (is_printable(key->key, key->size)) { - printf("%s=", key->key); - } else { - print_hex(key->key, key->size); - } - - if (is_printable(value->value, value->size)) { - printf("%s\n", value->value); - } else { - print_hex(value->value, value->size); - printf("\n"); - } - - sqfs_free(key); - sqfs_free(value); - } - - return 0; -} diff --git a/bin/rdsquashfs/fill_files.c b/bin/rdsquashfs/fill_files.c deleted file mode 100644 index 3104146..0000000 --- a/bin/rdsquashfs/fill_files.c +++ /dev/null @@ -1,186 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * fill_files.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "config.h" -#include "rdsquashfs.h" - -static struct file_ent { - char *path; - const sqfs_inode_generic_t *inode; -} *files = NULL; - -static size_t num_files = 0, max_files = 0; -static size_t block_size = 0; - -static int compare_files(const void *l, const void *r) -{ - sqfs_u32 lhs_frag_idx, lhs_frag_off, rhs_frag_idx, rhs_frag_off; - sqfs_u64 lhs_size, rhs_size, lhs_start, rhs_start; - const struct file_ent *lhs = l, *rhs = r; - - sqfs_inode_get_frag_location(lhs->inode, &lhs_frag_idx, &lhs_frag_off); - sqfs_inode_get_file_block_start(lhs->inode, &lhs_start); - sqfs_inode_get_file_size(lhs->inode, &lhs_size); - - sqfs_inode_get_frag_location(rhs->inode, &rhs_frag_idx, &rhs_frag_off); - sqfs_inode_get_file_block_start(rhs->inode, &rhs_start); - sqfs_inode_get_file_size(rhs->inode, &rhs_size); - - /* Files with fragments come first, ordered by ID. - In case of tie, files without data blocks come first, - and the others are ordered by start block. */ - if ((lhs_size % block_size) && (lhs_frag_off < block_size) && - (lhs_frag_idx != 0xFFFFFFFF)) { - if ((rhs_size % block_size) && (rhs_frag_off < block_size) && - (rhs_frag_idx != 0xFFFFFFFF)) - return -1; - - if (lhs_frag_idx < rhs_frag_idx) - return -1; - - if (lhs_frag_idx > rhs_frag_idx) - return 1; - - if (lhs_size < block_size) - return (rhs_size < block_size) ? 0 : -1; - - if (rhs_size < block_size) - return 1; - - goto order_by_start; - } - - if ((rhs_size % block_size) && (rhs_frag_off < block_size) && - (rhs_frag_idx != 0xFFFFFFFF)) - return 1; - - /* order the rest by start block */ -order_by_start: - return lhs_start < rhs_start ? -1 : lhs_start > rhs_start ? 1 : 0; -} - -static int add_file(const sqfs_tree_node_t *node) -{ - struct file_ent *new; - size_t new_sz; - char *path; - int ret; - - if (num_files == max_files) { - new_sz = max_files ? max_files * 2 : 256; - new = realloc(files, sizeof(files[0]) * new_sz); - - if (new == NULL) { - perror("expanding file list"); - return -1; - } - - files = new; - max_files = new_sz; - } - - ret = sqfs_tree_node_get_path(node, &path); - if (ret != 0) { - sqfs_perror(NULL, "assembling file path", ret); - return -1; - } - - if (canonicalize_name(path)) { - fprintf(stderr, "Invalid file path '%s'\n", path); - sqfs_free(path); - return -1; - } - - files[num_files].path = path; - files[num_files].inode = node->inode; - num_files++; - return 0; -} - -static void clear_file_list(void) -{ - size_t i; - - for (i = 0; i < num_files; ++i) - sqfs_free(files[i].path); - - free(files); - files = NULL; - num_files = 0; - max_files = 0; -} - -static int gen_file_list_dfs(const sqfs_tree_node_t *n) -{ - if (!is_filename_sane((const char *)n->name, true)) { - fprintf(stderr, "Found an entry named '%s', skipping.\n", - n->name); - return 0; - } - - if (S_ISREG(n->inode->base.mode)) - return add_file(n); - - if (S_ISDIR(n->inode->base.mode)) { - for (n = n->children; n != NULL; n = n->next) { - if (gen_file_list_dfs(n)) - return -1; - } - } - - return 0; -} - -static int fill_files(sqfs_data_reader_t *data, int flags) -{ - int ret, openflags; - ostream_t *fp; - size_t i; - - openflags = OSTREAM_OPEN_OVERWRITE; - - if (flags & UNPACK_NO_SPARSE) - openflags |= OSTREAM_OPEN_SPARSE; - - for (i = 0; i < num_files; ++i) { - fp = ostream_open_file(files[i].path, openflags); - if (fp == NULL) - return -1; - - if (!(flags & UNPACK_QUIET)) - printf("unpacking %s\n", files[i].path); - - ret = sqfs_data_reader_dump(files[i].path, data, files[i].inode, - fp, block_size); - if (ret == 0) - ret = ostream_flush(fp); - - sqfs_drop(fp); - if (ret) - return -1; - } - - return 0; -} - -int fill_unpacked_files(size_t blk_sz, const sqfs_tree_node_t *root, - sqfs_data_reader_t *data, int flags) -{ - int status; - - block_size = blk_sz; - - if (gen_file_list_dfs(root)) { - clear_file_list(); - return -1; - } - - qsort(files, num_files, sizeof(files[0]), compare_files); - - status = fill_files(data, flags); - clear_file_list(); - return status; -} diff --git a/bin/rdsquashfs/list_files.c b/bin/rdsquashfs/list_files.c deleted file mode 100644 index b1a0102..0000000 --- a/bin/rdsquashfs/list_files.c +++ /dev/null @@ -1,158 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * list_files.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "rdsquashfs.h" - -static void mode_to_str(sqfs_u16 mode, char *p) -{ - switch (mode & S_IFMT) { - case S_IFDIR: *(p++) = 'd'; break; - case S_IFCHR: *(p++) = 'c'; break; - case S_IFBLK: *(p++) = 'b'; break; - case S_IFREG: *(p++) = '-'; break; - case S_IFLNK: *(p++) = 'l'; break; - case S_IFSOCK: *(p++) = 's'; break; - case S_IFIFO: *(p++) = 'p'; break; - default: *(p++) = '?'; break; - } - - *(p++) = (mode & S_IRUSR) ? 'r' : '-'; - *(p++) = (mode & S_IWUSR) ? 'w' : '-'; - - switch (mode & (S_IXUSR | S_ISUID)) { - case S_IXUSR | S_ISUID: *(p++) = 's'; break; - case S_IXUSR: *(p++) = 'x'; break; - case S_ISUID: *(p++) = 'S'; break; - default: *(p++) = '-'; break; - } - - *(p++) = (mode & S_IRGRP) ? 'r' : '-'; - *(p++) = (mode & S_IWGRP) ? 'w' : '-'; - - switch (mode & (S_IXGRP | S_ISGID)) { - case S_IXGRP | S_ISGID: *(p++) = 's'; break; - case S_IXGRP: *(p++) = 'x'; break; - case S_ISGID: *(p++) = 'S'; break; - case 0: *(p++) = '-'; break; - default: break; - } - - *(p++) = (mode & S_IROTH) ? 'r' : '-'; - *(p++) = (mode & S_IWOTH) ? 'w' : '-'; - - switch (mode & (S_IXOTH | S_ISVTX)) { - case S_IXOTH | S_ISVTX: *(p++) = 't'; break; - case S_IXOTH: *(p++) = 'x'; break; - case S_ISVTX: *(p++) = 'T'; break; - case 0: *(p++) = '-'; break; - default: break; - } - - *p = '\0'; -} - -static int count_int_chars(unsigned int i) -{ - int count = 1; - - while (i > 10) { - ++count; - i /= 10; - } - - return count; -} - -static void print_node_size(const sqfs_tree_node_t *n, char *buffer) -{ - switch (n->inode->base.mode & S_IFMT) { - case S_IFLNK: - print_size(strlen((const char *)n->inode->extra), buffer, true); - break; - case S_IFREG: { - sqfs_u64 size; - sqfs_inode_get_file_size(n->inode, &size); - print_size(size, buffer, true); - break; - } - case S_IFDIR: - if (n->inode->base.type == SQFS_INODE_EXT_DIR) { - print_size(n->inode->data.dir_ext.size, buffer, true); - } else { - print_size(n->inode->data.dir.size, buffer, true); - } - break; - case S_IFBLK: - case S_IFCHR: { - sqfs_u32 devno; - - if (n->inode->base.type == SQFS_INODE_EXT_BDEV || - n->inode->base.type == SQFS_INODE_EXT_CDEV) { - devno = n->inode->data.dev_ext.devno; - } else { - devno = n->inode->data.dev.devno; - } - - sprintf(buffer, "%u:%u", major(devno), minor(devno)); - break; - } - default: - buffer[0] = '0'; - buffer[1] = '\0'; - break; - } -} - -void list_files(const sqfs_tree_node_t *node) -{ - int i, max_uid_chars = 0, max_gid_chars = 0, max_sz_chars = 0; - char modestr[12], sizestr[32]; - const sqfs_tree_node_t *n; - - if (S_ISDIR(node->inode->base.mode)) { - for (n = node->children; n != NULL; n = n->next) { - i = count_int_chars(n->uid); - max_uid_chars = i > max_uid_chars ? i : max_uid_chars; - - i = count_int_chars(n->gid); - max_gid_chars = i > max_gid_chars ? i : max_gid_chars; - - print_node_size(n, sizestr); - i = strlen(sizestr); - max_sz_chars = i > max_sz_chars ? i : max_sz_chars; - } - - for (n = node->children; n != NULL; n = n->next) { - mode_to_str(n->inode->base.mode, modestr); - print_node_size(n, sizestr); - - printf("%s %*u/%-*u %*s %s", modestr, - max_uid_chars, n->uid, - max_gid_chars, n->gid, - max_sz_chars, sizestr, - n->name); - - if (S_ISLNK(n->inode->base.mode)) { - printf(" -> %s\n", - (const char *)n->inode->extra); - } else { - fputc('\n', stdout); - } - } - } else { - mode_to_str(node->inode->base.mode, modestr); - print_node_size(node, sizestr); - - printf("%s %u/%u %s %s", modestr, - node->uid, node->gid, sizestr, node->name); - - if (S_ISLNK(node->inode->base.mode)) { - printf(" -> %s\n", (const char *)node->inode->extra); - } else { - fputc('\n', stdout); - } - } -} diff --git a/bin/rdsquashfs/options.c b/bin/rdsquashfs/options.c deleted file mode 100644 index dbb5e40..0000000 --- a/bin/rdsquashfs/options.c +++ /dev/null @@ -1,226 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * options.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "rdsquashfs.h" - -static struct option long_opts[] = { - { "list", required_argument, NULL, 'l' }, - { "cat", required_argument, NULL, 'c' }, - { "xattr", required_argument, NULL, 'x' }, - { "stat", required_argument, NULL, 's' }, - { "unpack-root", required_argument, NULL, 'p' }, - { "unpack-path", required_argument, NULL, 'u' }, - { "no-dev", no_argument, NULL, 'D' }, - { "no-sock", no_argument, NULL, 'S' }, - { "no-fifo", no_argument, NULL, 'F' }, - { "no-slink", no_argument, NULL, 'L' }, - { "no-empty-dir", no_argument, NULL, 'E' }, - { "no-sparse", no_argument, NULL, 'Z' }, -#ifdef HAVE_SYS_XATTR_H - { "set-xattr", no_argument, NULL, 'X' }, -#endif - { "set-times", no_argument, NULL, 'T' }, - { "describe", no_argument, NULL, 'd' }, - { "chmod", no_argument, NULL, 'C' }, - { "chown", no_argument, NULL, 'O' }, - { "quiet", no_argument, NULL, 'q' }, - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, 'V' }, - { NULL, 0, NULL, 0 }, -}; - -static const char *short_opts = - "l:c:u:p:x:s:DSFLCOEZTj:dqhV" -#ifdef HAVE_SYS_XATTR_H - "X" -#endif - ; - -static const char *help_string = -"Usage: rdsquashfs [OPTIONS] \n" -"\n" -"View or extract the contents of a squashfs image.\n" -"\n" -"Possible options:\n" -"\n" -" --list, -l Produce a directory listing for a given path in\n" -" the squashfs image.\n" -" --cat, -c If the specified path is a regular file in the,\n" -" image, dump its contents to stdout.\n" -" --xattr, -x Enumerate extended attributes associated with\n" -" an inode that the given path resolves to.\n" -" --unpack-path, -u Unpack this sub directory from the image. To\n" -" unpack everything, simply specify /.\n" -" --stat, -s Dump all information that can be extracted from\n" -" the inode coresponding to a path, including\n" -" SquashFS specific internals.\n" -" --describe, -d Produce a file listing from the image.\n" -"\n" -" --unpack-root, -p If used with --unpack-path, this is where the\n" -" data unpacked to. If used with --describe, this\n" -" is used as a prefix for the input path of\n" -" regular files.\n" -"\n" -" --no-dev, -D Do not unpack device special files.\n" -" --no-sock, -S Do not unpack socket files.\n" -" --no-fifo, -F Do not unpack named pipes.\n" -" --no-slink, -L Do not unpack symbolic links.\n" -" --no-empty-dir, -E Do not unpack directories that would end up\n" -" empty after applying the above rules.\n" -" --no-sparse, -Z Do not create sparse files, always write zero\n" -" blocks to disk.\n" -#ifdef HAVE_SYS_XATTR_H -" --set-xattr, -X When unpacking files to disk, set the extended\n" -" attributes from the squashfs image.\n" -#endif -" --set-times, -T When unpacking files to disk, set the create\n" -" and modify timestamps from the squashfs image.\n" -" --chmod, -C Change permission flags of unpacked files to\n" -" those store in the squashfs image.\n" -" --chown, -O Change ownership of unpacked files to the\n" -" UID/GID set in the squashfs image.\n" -" --quiet, -q Do not print out progress while unpacking.\n" -"\n" -" --help, -h Print help text and exit.\n" -" --version, -V Print version information and exit.\n" -"\n"; - -static char *get_path(char *old, const char *arg) -{ - char *path; - - free(old); - - path = strdup(arg); - if (path == NULL) { - perror("processing arguments"); - exit(EXIT_FAILURE); - } - - if (canonicalize_name(path)) { - fprintf(stderr, "Invalid path: %s\n", arg); - free(path); - exit(EXIT_FAILURE); - } - - return path; -} - -void process_command_line(options_t *opt, int argc, char **argv) -{ - int i; - - opt->op = OP_NONE; - opt->rdtree_flags = 0; - opt->flags = 0; - opt->cmdpath = NULL; - opt->unpack_root = NULL; - opt->image_name = NULL; - - for (;;) { - i = getopt_long(argc, argv, short_opts, long_opts, NULL); - if (i == -1) - break; - - switch (i) { - case 'D': - opt->rdtree_flags |= SQFS_TREE_NO_DEVICES; - break; - case 'S': - opt->rdtree_flags |= SQFS_TREE_NO_SOCKETS; - break; - case 'F': - opt->rdtree_flags |= SQFS_TREE_NO_FIFO; - break; - case 'L': - opt->rdtree_flags |= SQFS_TREE_NO_SLINKS; - break; - case 'E': - opt->rdtree_flags |= SQFS_TREE_NO_EMPTY; - break; - case 'C': - opt->flags |= UNPACK_CHMOD; - break; - case 'O': - opt->flags |= UNPACK_CHOWN; - break; - case 'Z': - opt->flags |= UNPACK_NO_SPARSE; - break; -#ifdef HAVE_SYS_XATTR_H - case 'X': - opt->flags |= UNPACK_SET_XATTR; - break; -#endif - case 'T': - opt->flags |= UNPACK_SET_TIMES; - break; - case 'c': - opt->op = OP_CAT; - opt->cmdpath = get_path(opt->cmdpath, optarg); - break; - case 'd': - opt->op = OP_DESCRIBE; - free(opt->cmdpath); - opt->cmdpath = NULL; - break; - case 'x': - opt->op = OP_RDATTR; - opt->cmdpath = get_path(opt->cmdpath, optarg); - break; - case 's': - opt->op = OP_STAT; - opt->cmdpath = get_path(opt->cmdpath, optarg); - break; - case 'l': - opt->op = OP_LS; - opt->cmdpath = get_path(opt->cmdpath, optarg); - break; - case 'p': - opt->unpack_root = optarg; - break; - case 'u': - opt->op = OP_UNPACK; - opt->cmdpath = get_path(opt->cmdpath, optarg); - break; - case 'q': - opt->flags |= UNPACK_QUIET; - break; - case 'h': - fputs(help_string, stdout); - free(opt->cmdpath); - exit(EXIT_SUCCESS); - case 'V': - print_version("rdsquashfs"); - free(opt->cmdpath); - exit(EXIT_SUCCESS); - default: - goto fail_arg; - } - } - - if (opt->op == OP_NONE) { - fputs("No operation specified\n", stderr); - goto fail_arg; - } - - if (opt->op == OP_LS || opt->op == OP_CAT || opt->op == OP_RDATTR || - opt->op == OP_STAT) { - opt->rdtree_flags |= SQFS_TREE_NO_RECURSE; - } - - if (optind >= argc) { - fputs("Missing image argument\n", stderr); - goto fail_arg; - } - - opt->image_name = argv[optind++]; - return; -fail_arg: - fputs("Try `rdsquashfs --help' for more information.\n", stderr); - free(opt->cmdpath); - exit(EXIT_FAILURE); -} diff --git a/bin/rdsquashfs/rdsquashfs.c b/bin/rdsquashfs/rdsquashfs.c deleted file mode 100644 index bdcc5a0..0000000 --- a/bin/rdsquashfs/rdsquashfs.c +++ /dev/null @@ -1,275 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * rdsquashfs.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "rdsquashfs.h" - -static sqfs_tree_node_t *list_merge(sqfs_tree_node_t *lhs, - sqfs_tree_node_t *rhs) -{ - sqfs_tree_node_t *it, *head = NULL, **next_ptr = &head; - - while (lhs != NULL && rhs != NULL) { - if (strcmp((const char *)lhs->name, - (const char *)rhs->name) <= 0) { - it = lhs; - lhs = lhs->next; - } else { - it = rhs; - rhs = rhs->next; - } - - *next_ptr = it; - next_ptr = &it->next; - } - - it = (lhs != NULL ? lhs : rhs); - *next_ptr = it; - return head; -} - -static sqfs_tree_node_t *list_sort(sqfs_tree_node_t *head) -{ - sqfs_tree_node_t *it, *half, *prev; - - it = half = prev = head; - - while (it != NULL) { - prev = half; - half = half->next; - it = it->next; - - if (it != NULL) - it = it->next; - } - - if (half == NULL) - return head; - - prev->next = NULL; - - return list_merge(list_sort(head), list_sort(half)); -} - -static int tree_sort(sqfs_tree_node_t *root) -{ - sqfs_tree_node_t *it; - - if (root->children == NULL) - return 0; - - root->children = list_sort(root->children); - - /* - XXX: not only an inconvenience but a security issue: e.g. we unpack a - SquashFS image that has a symlink pointing somewhere, and then a - sub-directory or file with the same name, the unpacker can be tricked - to follow the symlink and write anything, anywhere on the filesystem. - */ - for (it = root->children; it->next != NULL; it = it->next) { - if (strcmp((const char *)it->name, - (const char *)it->next->name) == 0) { - char *path; - int ret; - - ret = sqfs_tree_node_get_path(it, &path); - - if (ret == 0) { - fprintf(stderr, - "Entry '%s' found more than once!\n", - path); - } else { - fputs("Entry found more than once!\n", stderr); - } - - sqfs_free(path); - return -1; - } - } - - for (it = root->children; it != NULL; it = it->next) { - if (tree_sort(it)) - return -1; - } - - return 0; -} - -int main(int argc, char **argv) -{ - sqfs_xattr_reader_t *xattr = NULL; - sqfs_data_reader_t *data = NULL; - sqfs_dir_reader_t *dirrd = NULL; - sqfs_compressor_t *cmp = NULL; - sqfs_id_table_t *idtbl = NULL; - sqfs_compressor_config_t cfg; - sqfs_tree_node_t *n = NULL; - int status = EXIT_FAILURE; - sqfs_file_t *file = NULL; - sqfs_super_t super; - options_t opt; - int ret; - - process_command_line(&opt, argc, argv); - - file = sqfs_open_file(opt.image_name, SQFS_FILE_OPEN_READ_ONLY); - if (file == NULL) { - perror(opt.image_name); - goto out; - } - - ret = sqfs_super_read(&super, file); - if (ret) { - sqfs_perror(opt.image_name, "reading super block", ret); - goto out; - } - - sqfs_compressor_config_init(&cfg, super.compression_id, - super.block_size, - SQFS_COMP_FLAG_UNCOMPRESS); - - ret = sqfs_compressor_create(&cfg, &cmp); - -#ifdef WITH_LZO - if (super.compression_id == SQFS_COMP_LZO && ret != 0) - ret = lzo_compressor_create(&cfg, &cmp); -#endif - - if (ret != 0) { - sqfs_perror(opt.image_name, "creating compressor", ret); - goto out; - } - - if (!(super.flags & SQFS_FLAG_NO_XATTRS)) { - xattr = sqfs_xattr_reader_create(0); - if (xattr == NULL) { - sqfs_perror(opt.image_name, "creating xattr reader", - SQFS_ERROR_ALLOC); - goto out; - } - - ret = sqfs_xattr_reader_load(xattr, &super, file, cmp); - if (ret) { - sqfs_perror(opt.image_name, "loading xattr table", - ret); - goto out; - } - } - - idtbl = sqfs_id_table_create(0); - if (idtbl == NULL) { - sqfs_perror(opt.image_name, "creating ID table", - SQFS_ERROR_ALLOC); - goto out; - } - - ret = sqfs_id_table_read(idtbl, file, &super, cmp); - if (ret) { - sqfs_perror(opt.image_name, "loading ID table", ret); - goto out; - } - - dirrd = sqfs_dir_reader_create(&super, cmp, file, 0); - if (dirrd == NULL) { - sqfs_perror(opt.image_name, "creating dir reader", - SQFS_ERROR_ALLOC); - goto out; - } - - data = sqfs_data_reader_create(file, super.block_size, cmp, 0); - if (data == NULL) { - sqfs_perror(opt.image_name, "creating data reader", - SQFS_ERROR_ALLOC); - goto out; - } - - ret = sqfs_data_reader_load_fragment_table(data, &super); - if (ret) { - sqfs_perror(opt.image_name, "loading fragment table", ret); - goto out; - } - - ret = sqfs_dir_reader_get_full_hierarchy(dirrd, idtbl, opt.cmdpath, - opt.rdtree_flags, &n); - if (ret) { - sqfs_perror(opt.image_name, "reading filesystem tree", ret); - goto out; - } - - switch (opt.op) { - case OP_LS: - list_files(n); - break; - case OP_STAT: - if (stat_file(n)) - goto out; - break; - case OP_CAT: { - ostream_t *fp; - - if (!S_ISREG(n->inode->base.mode)) { - fprintf(stderr, "/%s: not a regular file\n", - opt.cmdpath); - goto out; - } - - fp = ostream_open_stdout(); - if (fp == NULL) - goto out; - - ret = sqfs_data_reader_dump(opt.cmdpath, data, n->inode, - fp, super.block_size); - sqfs_drop(fp); - if (ret) - goto out; - break; - } - case OP_UNPACK: - if (tree_sort(n)) - goto out; - - if (opt.unpack_root != NULL) { - if (mkdir_p(opt.unpack_root)) - goto out; - - if (chdir(opt.unpack_root)) { - perror(opt.unpack_root); - goto out; - } - } - - if (restore_fstree(n, opt.flags)) - goto out; - - if (fill_unpacked_files(super.block_size, n, data, opt.flags)) - goto out; - - if (update_tree_attribs(xattr, n, opt.flags)) - goto out; - break; - case OP_DESCRIBE: - if (describe_tree(n, opt.unpack_root)) - goto out; - break; - case OP_RDATTR: - if (dump_xattrs(xattr, n->inode)) - goto out; - break; - default: - break; - } - - status = EXIT_SUCCESS; -out: - sqfs_dir_tree_destroy(n); - sqfs_drop(data); - sqfs_drop(dirrd); - sqfs_drop(idtbl); - sqfs_drop(xattr); - sqfs_drop(cmp); - sqfs_drop(file); - free(opt.cmdpath); - return status; -} diff --git a/bin/rdsquashfs/rdsquashfs.h b/bin/rdsquashfs/rdsquashfs.h deleted file mode 100644 index 56bb836..0000000 --- a/bin/rdsquashfs/rdsquashfs.h +++ /dev/null @@ -1,82 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * rdsquashfs.h - * - * Copyright (C) 2019 David Oberhollenzer - */ -#ifndef RDSQUASHFS_H -#define RDSQUASHFS_H - -#include "config.h" -#include "common.h" -#include "fstree.h" -#include "util/util.h" - -#ifdef _WIN32 -#define WIN32_LEAN_AND_MEAN -#include -#endif -#ifdef HAVE_SYS_XATTR_H -#include - -#if defined(__APPLE__) && defined(__MACH__) -#define lsetxattr(path, name, value, size, flags) \ - setxattr(path, name, value, size, 0, flags | XATTR_NOFOLLOW) -#endif -#endif -#include -#include -#include -#include -#include -#include -#include -#include - -enum UNPACK_FLAGS { - UNPACK_CHMOD = 0x01, - UNPACK_CHOWN = 0x02, - UNPACK_QUIET = 0x04, - UNPACK_NO_SPARSE = 0x08, - UNPACK_SET_XATTR = 0x10, - UNPACK_SET_TIMES = 0x20, -}; - -enum { - OP_NONE = 0, - OP_LS, - OP_CAT, - OP_UNPACK, - OP_DESCRIBE, - OP_RDATTR, - OP_STAT, -}; - -typedef struct { - int op; - int rdtree_flags; - int flags; - char *cmdpath; - const char *unpack_root; - const char *image_name; -} options_t; - -void list_files(const sqfs_tree_node_t *node); - -int stat_file(const sqfs_tree_node_t *node); - -int restore_fstree(sqfs_tree_node_t *root, int flags); - -int update_tree_attribs(sqfs_xattr_reader_t *xattr, - const sqfs_tree_node_t *root, int flags); - -int fill_unpacked_files(size_t blk_sz, const sqfs_tree_node_t *root, - sqfs_data_reader_t *data, int flags); - -int describe_tree(const sqfs_tree_node_t *root, const char *unpack_root); - -int dump_xattrs(sqfs_xattr_reader_t *xattr, const sqfs_inode_generic_t *inode); - -void process_command_line(options_t *opt, int argc, char **argv); - -#endif /* RDSQUASHFS_H */ diff --git a/bin/rdsquashfs/restore_fstree.c b/bin/rdsquashfs/restore_fstree.c deleted file mode 100644 index ea9d4f1..0000000 --- a/bin/rdsquashfs/restore_fstree.c +++ /dev/null @@ -1,336 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * restore_fstree.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "rdsquashfs.h" - -#ifdef _WIN32 -static int create_node(const sqfs_tree_node_t *n, const char *name, int flags) -{ - WCHAR *wpath; - HANDLE fh; - (void)flags; - - wpath = path_to_windows(name); - if (wpath == NULL) - return -1; - - switch (n->inode->base.mode & S_IFMT) { - case S_IFDIR: - if (!CreateDirectoryW(wpath, NULL)) { - if (GetLastError() != ERROR_ALREADY_EXISTS) - goto fail; - } - break; - case S_IFREG: - fh = CreateFileW(wpath, GENERIC_READ, - FILE_SHARE_READ | FILE_SHARE_WRITE, - NULL, CREATE_NEW, 0, NULL); - - if (fh == INVALID_HANDLE_VALUE) - goto fail; - - CloseHandle(fh); - break; - default: - break; - } - - free(wpath); - return 0; -fail: { - DWORD err = GetLastError(); - free(wpath); - SetLastError(err); - w32_perror(name); - - if (err == ERROR_FILE_EXISTS) { - fputs("\nHINT: this could be caused by case " - "sensitivity on Windows.\n", stderr); - } - return -1; -} -} -#else -static int create_node(const sqfs_tree_node_t *n, const char *name, int flags) -{ - sqfs_u32 devno; - int fd, mode; - - switch (n->inode->base.mode & S_IFMT) { - case S_IFDIR: - if (mkdir(name, 0755) && errno != EEXIST) { - fprintf(stderr, "mkdir %s: %s\n", - name, strerror(errno)); - return -1; - } - break; - case S_IFLNK: - if (symlink((const char *)n->inode->extra, name)) { - fprintf(stderr, "ln -s %s %s: %s\n", - (const char *)n->inode->extra, name, - strerror(errno)); - return -1; - } - break; - case S_IFSOCK: - case S_IFIFO: - if (mknod(name, (n->inode->base.mode & S_IFMT) | 0700, 0)) { - fprintf(stderr, "creating %s: %s\n", - name, strerror(errno)); - return -1; - } - break; - case S_IFBLK: - case S_IFCHR: - if (n->inode->base.type == SQFS_INODE_EXT_BDEV || - n->inode->base.type == SQFS_INODE_EXT_CDEV) { - devno = n->inode->data.dev_ext.devno; - } else { - devno = n->inode->data.dev.devno; - } - - if (mknod(name, n->inode->base.mode & S_IFMT, devno)) { - fprintf(stderr, "creating device %s: %s\n", - name, strerror(errno)); - return -1; - } - break; - case S_IFREG: - if (flags & UNPACK_CHMOD) { - mode = (n->inode->base.mode & ~S_IFMT) | 0200; - } else { - mode = 0644; - } - - fd = open(name, O_WRONLY | O_CREAT | O_EXCL, mode); - - if (fd < 0) { - fprintf(stderr, "creating %s: %s\n", - name, strerror(errno)); - return -1; - } - - close(fd); - break; - default: - break; - } - - return 0; -} -#endif - -static int create_node_dfs(const sqfs_tree_node_t *n, int flags) -{ - const sqfs_tree_node_t *c; - char *name; - int ret; - - if (!is_filename_sane((const char *)n->name, true)) { - fprintf(stderr, "Found an entry named '%s', skipping.\n", - n->name); - return 0; - } - - ret = sqfs_tree_node_get_path(n, &name); - if (ret != 0) { - sqfs_perror((const char *)n->name, - "constructing full path", ret); - return -1; - } - - ret = canonicalize_name(name); - assert(ret == 0); - - if (!(flags & UNPACK_QUIET)) - printf("creating %s\n", name); - - ret = create_node(n, name, flags); - sqfs_free(name); - if (ret) - return -1; - - if (S_ISDIR(n->inode->base.mode)) { - for (c = n->children; c != NULL; c = c->next) { - if (create_node_dfs(c, flags)) - return -1; - } - } - return 0; -} - -#ifdef HAVE_SYS_XATTR_H -static int set_xattr(const char *path, sqfs_xattr_reader_t *xattr, - const sqfs_tree_node_t *n) -{ - sqfs_xattr_value_t *value; - sqfs_xattr_entry_t *key; - sqfs_xattr_id_t desc; - sqfs_u32 index; - size_t i; - int ret; - - sqfs_inode_get_xattr_index(n->inode, &index); - - if (index == 0xFFFFFFFF) - return 0; - - if (sqfs_xattr_reader_get_desc(xattr, index, &desc)) { - fputs("Error resolving xattr index\n", stderr); - return -1; - } - - if (sqfs_xattr_reader_seek_kv(xattr, &desc)) { - fputs("Error locating xattr key-value pairs\n", stderr); - return -1; - } - - for (i = 0; i < desc.count; ++i) { - if (sqfs_xattr_reader_read_key(xattr, &key)) { - fputs("Error reading xattr key\n", stderr); - return -1; - } - - if (sqfs_xattr_reader_read_value(xattr, key, &value)) { - fputs("Error reading xattr value\n", stderr); - sqfs_free(key); - return -1; - } - - ret = lsetxattr(path, (const char *)key->key, - value->value, value->size, 0); - if (ret) { - fprintf(stderr, "setting xattr '%s' on %s: %s\n", - key->key, path, strerror(errno)); - } - - sqfs_free(key); - sqfs_free(value); - if (ret) - return -1; - } - - return 0; -} -#endif - -static int set_attribs(sqfs_xattr_reader_t *xattr, - const sqfs_tree_node_t *n, int flags) -{ - const sqfs_tree_node_t *c; - char *path; - int ret; - - if (!is_filename_sane((const char *)n->name, true)) - return 0; - - if (S_ISDIR(n->inode->base.mode)) { - for (c = n->children; c != NULL; c = c->next) { - if (set_attribs(xattr, c, flags)) - return -1; - } - } - - ret = sqfs_tree_node_get_path(n, &path); - if (ret != 0) { - sqfs_perror(NULL, "reconstructing full path", ret); - return -1; - } - - ret = canonicalize_name(path); - assert(ret == 0); - -#ifdef HAVE_SYS_XATTR_H - if ((flags & UNPACK_SET_XATTR) && xattr != NULL) { - if (set_xattr(path, xattr, n)) - goto fail; - } -#endif - -#ifndef _WIN32 - if (flags & UNPACK_SET_TIMES) { - struct timespec times[2]; - - memset(times, 0, sizeof(times)); - times[0].tv_sec = n->inode->base.mod_time; - times[1].tv_sec = n->inode->base.mod_time; - - if (utimensat(AT_FDCWD, path, times, AT_SYMLINK_NOFOLLOW)) { - fprintf(stderr, "setting timestamp on %s: %s\n", - path, strerror(errno)); - goto fail; - } - } -#endif - if (flags & UNPACK_CHOWN) { - if (fchownat(AT_FDCWD, path, n->uid, n->gid, - AT_SYMLINK_NOFOLLOW)) { - fprintf(stderr, "chown %s: %s\n", - path, strerror(errno)); - goto fail; - } - } - - if (flags & UNPACK_CHMOD && !S_ISLNK(n->inode->base.mode)) { - if (fchmodat(AT_FDCWD, path, - n->inode->base.mode & ~S_IFMT, 0)) { - fprintf(stderr, "chmod %s: %s\n", - path, strerror(errno)); - goto fail; - } - } - - sqfs_free(path); - return 0; -fail: - sqfs_free(path); - return -1; -} - -int restore_fstree(sqfs_tree_node_t *root, int flags) -{ - sqfs_tree_node_t *n, *old_parent; - - /* make sure fstree_get_path() stops at this node */ - old_parent = root->parent; - root->parent = NULL; - - if (S_ISDIR(root->inode->base.mode)) { - for (n = root->children; n != NULL; n = n->next) { - if (create_node_dfs(n, flags)) - return -1; - } - } else { - if (create_node_dfs(root, flags)) - return -1; - } - - root->parent = old_parent; - return 0; -} - -int update_tree_attribs(sqfs_xattr_reader_t *xattr, - const sqfs_tree_node_t *root, int flags) -{ - const sqfs_tree_node_t *n; - - if ((flags & (UNPACK_CHOWN | UNPACK_CHMOD | - UNPACK_SET_TIMES | UNPACK_SET_XATTR)) == 0) { - return 0; - } - - if (S_ISDIR(root->inode->base.mode)) { - for (n = root->children; n != NULL; n = n->next) { - if (set_attribs(xattr, n, flags)) - return -1; - } - } else { - if (set_attribs(xattr, root, flags)) - return -1; - } - - return 0; -} diff --git a/bin/rdsquashfs/src/describe.c b/bin/rdsquashfs/src/describe.c new file mode 100644 index 0000000..540b126 --- /dev/null +++ b/bin/rdsquashfs/src/describe.c @@ -0,0 +1,139 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * describe.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "rdsquashfs.h" + +static int print_name(const sqfs_tree_node_t *n, bool dont_escape) +{ + char *start, *ptr, *name; + int ret; + + ret = sqfs_tree_node_get_path(n, &name); + if (ret != 0) { + sqfs_perror(NULL, "Recovering file path of tree node", ret); + return -1; + } + + if (canonicalize_name(name) != 0) { + fprintf(stderr, "Error sanitizing file path '%s'\n", name); + sqfs_free(name); + return -1; + } + + if (dont_escape || (strchr(name, ' ') == NULL && + strchr(name, '"') == NULL)) { + fputs(name, stdout); + } else { + fputc('"', stdout); + + ptr = strchr(name, '"'); + + if (ptr != NULL) { + start = name; + + do { + fwrite(start, 1, ptr - start, stdout); + fputs("\\\"", stdout); + start = ptr + 1; + ptr = strchr(start, '"'); + } while (ptr != NULL); + + fputs(start, stdout); + } else { + fputs(name, stdout); + } + + fputc('"', stdout); + } + + sqfs_free(name); + return 0; +} + +static void print_perm(const sqfs_tree_node_t *n) +{ + printf(" 0%o %u %u", (unsigned int)n->inode->base.mode & (~S_IFMT), + n->uid, n->gid); +} + +static int print_simple(const char *type, const sqfs_tree_node_t *n, + const char *extra) +{ + printf("%s ", type); + if (print_name(n, false)) + return -1; + print_perm(n); + if (extra != NULL) + printf(" %s", extra); + fputc('\n', stdout); + return 0; +} + +int describe_tree(const sqfs_tree_node_t *root, const char *unpack_root) +{ + const sqfs_tree_node_t *n; + + if (!is_filename_sane((const char *)root->name, false)) { + fprintf(stderr, "Encountered illegal file name '%s'\n", + root->name); + return -1; + } + + switch (root->inode->base.mode & S_IFMT) { + case S_IFSOCK: + return print_simple("sock", root, NULL); + case S_IFLNK: + return print_simple("slink", root, + (const char *)root->inode->extra); + case S_IFIFO: + return print_simple("pipe", root, NULL); + case S_IFREG: + if (unpack_root == NULL) + return print_simple("file", root, NULL); + + fputs("file ", stdout); + if (print_name(root, false)) + return -1; + print_perm(root); + printf(" %s/", unpack_root); + if (print_name(root, true)) + return -1; + fputc('\n', stdout); + break; + case S_IFCHR: + case S_IFBLK: { + char buffer[32]; + sqfs_u32 devno; + + if (root->inode->base.type == SQFS_INODE_EXT_BDEV || + root->inode->base.type == SQFS_INODE_EXT_CDEV) { + devno = root->inode->data.dev_ext.devno; + } else { + devno = root->inode->data.dev.devno; + } + + sprintf(buffer, "%c %u %u", + S_ISCHR(root->inode->base.mode) ? 'c' : 'b', + major(devno), minor(devno)); + return print_simple("nod", root, buffer); + } + case S_IFDIR: + if (root->name[0] != '\0') { + if (print_simple("dir", root, NULL)) + return -1; + } + + for (n = root->children; n != NULL; n = n->next) { + if (describe_tree(n, unpack_root)) + return -1; + } + break; + default: + break; + } + + return 0; +} diff --git a/bin/rdsquashfs/src/dump_xattrs.c b/bin/rdsquashfs/src/dump_xattrs.c new file mode 100644 index 0000000..9dbe437 --- /dev/null +++ b/bin/rdsquashfs/src/dump_xattrs.c @@ -0,0 +1,120 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * dump_xattrs.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "rdsquashfs.h" + +static void print_hex(const sqfs_u8 *value, size_t len) +{ + printf("0x"); + + while (len--) + printf("%02X", *(value++)); +} + +static bool is_printable(const sqfs_u8 *value, size_t len) +{ + size_t utf8_cont = 0; + sqfs_u8 x; + + while (len--) { + x = *(value++); + + if (utf8_cont > 0) { + if ((x & 0xC0) != 0x80) + return false; + + --utf8_cont; + } else { + if (x < 0x80) { + if (x < 0x20) { + if (x >= 0x07 && x <= 0x0D) + continue; + if (x == 0x00) + continue; + return false; + } + + if (x == 0x7F) + return false; + } + + if ((x & 0xE0) == 0xC0) { + utf8_cont = 1; + } else if ((x & 0xF0) == 0xE0) { + utf8_cont = 2; + } else if ((x & 0xF8) == 0xF0) { + utf8_cont = 3; + } else if ((x & 0xFC) == 0xF8) { + utf8_cont = 4; + } else if ((x & 0xFE) == 0xFC) { + utf8_cont = 5; + } + + if (utf8_cont > 0 && len < utf8_cont) + return false; + } + } + + return true; +} + +int dump_xattrs(sqfs_xattr_reader_t *xattr, const sqfs_inode_generic_t *inode) +{ + sqfs_xattr_value_t *value; + sqfs_xattr_entry_t *key; + sqfs_xattr_id_t desc; + sqfs_u32 index; + size_t i; + + if (xattr == NULL) + return 0; + + sqfs_inode_get_xattr_index(inode, &index); + + if (index == 0xFFFFFFFF) + return 0; + + if (sqfs_xattr_reader_get_desc(xattr, index, &desc)) { + fputs("Error resolving xattr index\n", stderr); + return -1; + } + + if (sqfs_xattr_reader_seek_kv(xattr, &desc)) { + fputs("Error locating xattr key-value pairs\n", stderr); + return -1; + } + + for (i = 0; i < desc.count; ++i) { + if (sqfs_xattr_reader_read_key(xattr, &key)) { + fputs("Error reading xattr key\n", stderr); + return -1; + } + + if (sqfs_xattr_reader_read_value(xattr, key, &value)) { + fputs("Error reading xattr value\n", stderr); + sqfs_free(key); + return -1; + } + + if (is_printable(key->key, key->size)) { + printf("%s=", key->key); + } else { + print_hex(key->key, key->size); + } + + if (is_printable(value->value, value->size)) { + printf("%s\n", value->value); + } else { + print_hex(value->value, value->size); + printf("\n"); + } + + sqfs_free(key); + sqfs_free(value); + } + + return 0; +} diff --git a/bin/rdsquashfs/src/fill_files.c b/bin/rdsquashfs/src/fill_files.c new file mode 100644 index 0000000..3104146 --- /dev/null +++ b/bin/rdsquashfs/src/fill_files.c @@ -0,0 +1,186 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * fill_files.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "config.h" +#include "rdsquashfs.h" + +static struct file_ent { + char *path; + const sqfs_inode_generic_t *inode; +} *files = NULL; + +static size_t num_files = 0, max_files = 0; +static size_t block_size = 0; + +static int compare_files(const void *l, const void *r) +{ + sqfs_u32 lhs_frag_idx, lhs_frag_off, rhs_frag_idx, rhs_frag_off; + sqfs_u64 lhs_size, rhs_size, lhs_start, rhs_start; + const struct file_ent *lhs = l, *rhs = r; + + sqfs_inode_get_frag_location(lhs->inode, &lhs_frag_idx, &lhs_frag_off); + sqfs_inode_get_file_block_start(lhs->inode, &lhs_start); + sqfs_inode_get_file_size(lhs->inode, &lhs_size); + + sqfs_inode_get_frag_location(rhs->inode, &rhs_frag_idx, &rhs_frag_off); + sqfs_inode_get_file_block_start(rhs->inode, &rhs_start); + sqfs_inode_get_file_size(rhs->inode, &rhs_size); + + /* Files with fragments come first, ordered by ID. + In case of tie, files without data blocks come first, + and the others are ordered by start block. */ + if ((lhs_size % block_size) && (lhs_frag_off < block_size) && + (lhs_frag_idx != 0xFFFFFFFF)) { + if ((rhs_size % block_size) && (rhs_frag_off < block_size) && + (rhs_frag_idx != 0xFFFFFFFF)) + return -1; + + if (lhs_frag_idx < rhs_frag_idx) + return -1; + + if (lhs_frag_idx > rhs_frag_idx) + return 1; + + if (lhs_size < block_size) + return (rhs_size < block_size) ? 0 : -1; + + if (rhs_size < block_size) + return 1; + + goto order_by_start; + } + + if ((rhs_size % block_size) && (rhs_frag_off < block_size) && + (rhs_frag_idx != 0xFFFFFFFF)) + return 1; + + /* order the rest by start block */ +order_by_start: + return lhs_start < rhs_start ? -1 : lhs_start > rhs_start ? 1 : 0; +} + +static int add_file(const sqfs_tree_node_t *node) +{ + struct file_ent *new; + size_t new_sz; + char *path; + int ret; + + if (num_files == max_files) { + new_sz = max_files ? max_files * 2 : 256; + new = realloc(files, sizeof(files[0]) * new_sz); + + if (new == NULL) { + perror("expanding file list"); + return -1; + } + + files = new; + max_files = new_sz; + } + + ret = sqfs_tree_node_get_path(node, &path); + if (ret != 0) { + sqfs_perror(NULL, "assembling file path", ret); + return -1; + } + + if (canonicalize_name(path)) { + fprintf(stderr, "Invalid file path '%s'\n", path); + sqfs_free(path); + return -1; + } + + files[num_files].path = path; + files[num_files].inode = node->inode; + num_files++; + return 0; +} + +static void clear_file_list(void) +{ + size_t i; + + for (i = 0; i < num_files; ++i) + sqfs_free(files[i].path); + + free(files); + files = NULL; + num_files = 0; + max_files = 0; +} + +static int gen_file_list_dfs(const sqfs_tree_node_t *n) +{ + if (!is_filename_sane((const char *)n->name, true)) { + fprintf(stderr, "Found an entry named '%s', skipping.\n", + n->name); + return 0; + } + + if (S_ISREG(n->inode->base.mode)) + return add_file(n); + + if (S_ISDIR(n->inode->base.mode)) { + for (n = n->children; n != NULL; n = n->next) { + if (gen_file_list_dfs(n)) + return -1; + } + } + + return 0; +} + +static int fill_files(sqfs_data_reader_t *data, int flags) +{ + int ret, openflags; + ostream_t *fp; + size_t i; + + openflags = OSTREAM_OPEN_OVERWRITE; + + if (flags & UNPACK_NO_SPARSE) + openflags |= OSTREAM_OPEN_SPARSE; + + for (i = 0; i < num_files; ++i) { + fp = ostream_open_file(files[i].path, openflags); + if (fp == NULL) + return -1; + + if (!(flags & UNPACK_QUIET)) + printf("unpacking %s\n", files[i].path); + + ret = sqfs_data_reader_dump(files[i].path, data, files[i].inode, + fp, block_size); + if (ret == 0) + ret = ostream_flush(fp); + + sqfs_drop(fp); + if (ret) + return -1; + } + + return 0; +} + +int fill_unpacked_files(size_t blk_sz, const sqfs_tree_node_t *root, + sqfs_data_reader_t *data, int flags) +{ + int status; + + block_size = blk_sz; + + if (gen_file_list_dfs(root)) { + clear_file_list(); + return -1; + } + + qsort(files, num_files, sizeof(files[0]), compare_files); + + status = fill_files(data, flags); + clear_file_list(); + return status; +} diff --git a/bin/rdsquashfs/src/list_files.c b/bin/rdsquashfs/src/list_files.c new file mode 100644 index 0000000..b1a0102 --- /dev/null +++ b/bin/rdsquashfs/src/list_files.c @@ -0,0 +1,158 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * list_files.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "rdsquashfs.h" + +static void mode_to_str(sqfs_u16 mode, char *p) +{ + switch (mode & S_IFMT) { + case S_IFDIR: *(p++) = 'd'; break; + case S_IFCHR: *(p++) = 'c'; break; + case S_IFBLK: *(p++) = 'b'; break; + case S_IFREG: *(p++) = '-'; break; + case S_IFLNK: *(p++) = 'l'; break; + case S_IFSOCK: *(p++) = 's'; break; + case S_IFIFO: *(p++) = 'p'; break; + default: *(p++) = '?'; break; + } + + *(p++) = (mode & S_IRUSR) ? 'r' : '-'; + *(p++) = (mode & S_IWUSR) ? 'w' : '-'; + + switch (mode & (S_IXUSR | S_ISUID)) { + case S_IXUSR | S_ISUID: *(p++) = 's'; break; + case S_IXUSR: *(p++) = 'x'; break; + case S_ISUID: *(p++) = 'S'; break; + default: *(p++) = '-'; break; + } + + *(p++) = (mode & S_IRGRP) ? 'r' : '-'; + *(p++) = (mode & S_IWGRP) ? 'w' : '-'; + + switch (mode & (S_IXGRP | S_ISGID)) { + case S_IXGRP | S_ISGID: *(p++) = 's'; break; + case S_IXGRP: *(p++) = 'x'; break; + case S_ISGID: *(p++) = 'S'; break; + case 0: *(p++) = '-'; break; + default: break; + } + + *(p++) = (mode & S_IROTH) ? 'r' : '-'; + *(p++) = (mode & S_IWOTH) ? 'w' : '-'; + + switch (mode & (S_IXOTH | S_ISVTX)) { + case S_IXOTH | S_ISVTX: *(p++) = 't'; break; + case S_IXOTH: *(p++) = 'x'; break; + case S_ISVTX: *(p++) = 'T'; break; + case 0: *(p++) = '-'; break; + default: break; + } + + *p = '\0'; +} + +static int count_int_chars(unsigned int i) +{ + int count = 1; + + while (i > 10) { + ++count; + i /= 10; + } + + return count; +} + +static void print_node_size(const sqfs_tree_node_t *n, char *buffer) +{ + switch (n->inode->base.mode & S_IFMT) { + case S_IFLNK: + print_size(strlen((const char *)n->inode->extra), buffer, true); + break; + case S_IFREG: { + sqfs_u64 size; + sqfs_inode_get_file_size(n->inode, &size); + print_size(size, buffer, true); + break; + } + case S_IFDIR: + if (n->inode->base.type == SQFS_INODE_EXT_DIR) { + print_size(n->inode->data.dir_ext.size, buffer, true); + } else { + print_size(n->inode->data.dir.size, buffer, true); + } + break; + case S_IFBLK: + case S_IFCHR: { + sqfs_u32 devno; + + if (n->inode->base.type == SQFS_INODE_EXT_BDEV || + n->inode->base.type == SQFS_INODE_EXT_CDEV) { + devno = n->inode->data.dev_ext.devno; + } else { + devno = n->inode->data.dev.devno; + } + + sprintf(buffer, "%u:%u", major(devno), minor(devno)); + break; + } + default: + buffer[0] = '0'; + buffer[1] = '\0'; + break; + } +} + +void list_files(const sqfs_tree_node_t *node) +{ + int i, max_uid_chars = 0, max_gid_chars = 0, max_sz_chars = 0; + char modestr[12], sizestr[32]; + const sqfs_tree_node_t *n; + + if (S_ISDIR(node->inode->base.mode)) { + for (n = node->children; n != NULL; n = n->next) { + i = count_int_chars(n->uid); + max_uid_chars = i > max_uid_chars ? i : max_uid_chars; + + i = count_int_chars(n->gid); + max_gid_chars = i > max_gid_chars ? i : max_gid_chars; + + print_node_size(n, sizestr); + i = strlen(sizestr); + max_sz_chars = i > max_sz_chars ? i : max_sz_chars; + } + + for (n = node->children; n != NULL; n = n->next) { + mode_to_str(n->inode->base.mode, modestr); + print_node_size(n, sizestr); + + printf("%s %*u/%-*u %*s %s", modestr, + max_uid_chars, n->uid, + max_gid_chars, n->gid, + max_sz_chars, sizestr, + n->name); + + if (S_ISLNK(n->inode->base.mode)) { + printf(" -> %s\n", + (const char *)n->inode->extra); + } else { + fputc('\n', stdout); + } + } + } else { + mode_to_str(node->inode->base.mode, modestr); + print_node_size(node, sizestr); + + printf("%s %u/%u %s %s", modestr, + node->uid, node->gid, sizestr, node->name); + + if (S_ISLNK(node->inode->base.mode)) { + printf(" -> %s\n", (const char *)node->inode->extra); + } else { + fputc('\n', stdout); + } + } +} diff --git a/bin/rdsquashfs/src/options.c b/bin/rdsquashfs/src/options.c new file mode 100644 index 0000000..dbb5e40 --- /dev/null +++ b/bin/rdsquashfs/src/options.c @@ -0,0 +1,226 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * options.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "rdsquashfs.h" + +static struct option long_opts[] = { + { "list", required_argument, NULL, 'l' }, + { "cat", required_argument, NULL, 'c' }, + { "xattr", required_argument, NULL, 'x' }, + { "stat", required_argument, NULL, 's' }, + { "unpack-root", required_argument, NULL, 'p' }, + { "unpack-path", required_argument, NULL, 'u' }, + { "no-dev", no_argument, NULL, 'D' }, + { "no-sock", no_argument, NULL, 'S' }, + { "no-fifo", no_argument, NULL, 'F' }, + { "no-slink", no_argument, NULL, 'L' }, + { "no-empty-dir", no_argument, NULL, 'E' }, + { "no-sparse", no_argument, NULL, 'Z' }, +#ifdef HAVE_SYS_XATTR_H + { "set-xattr", no_argument, NULL, 'X' }, +#endif + { "set-times", no_argument, NULL, 'T' }, + { "describe", no_argument, NULL, 'd' }, + { "chmod", no_argument, NULL, 'C' }, + { "chown", no_argument, NULL, 'O' }, + { "quiet", no_argument, NULL, 'q' }, + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, 'V' }, + { NULL, 0, NULL, 0 }, +}; + +static const char *short_opts = + "l:c:u:p:x:s:DSFLCOEZTj:dqhV" +#ifdef HAVE_SYS_XATTR_H + "X" +#endif + ; + +static const char *help_string = +"Usage: rdsquashfs [OPTIONS] \n" +"\n" +"View or extract the contents of a squashfs image.\n" +"\n" +"Possible options:\n" +"\n" +" --list, -l Produce a directory listing for a given path in\n" +" the squashfs image.\n" +" --cat, -c If the specified path is a regular file in the,\n" +" image, dump its contents to stdout.\n" +" --xattr, -x Enumerate extended attributes associated with\n" +" an inode that the given path resolves to.\n" +" --unpack-path, -u Unpack this sub directory from the image. To\n" +" unpack everything, simply specify /.\n" +" --stat, -s Dump all information that can be extracted from\n" +" the inode coresponding to a path, including\n" +" SquashFS specific internals.\n" +" --describe, -d Produce a file listing from the image.\n" +"\n" +" --unpack-root, -p If used with --unpack-path, this is where the\n" +" data unpacked to. If used with --describe, this\n" +" is used as a prefix for the input path of\n" +" regular files.\n" +"\n" +" --no-dev, -D Do not unpack device special files.\n" +" --no-sock, -S Do not unpack socket files.\n" +" --no-fifo, -F Do not unpack named pipes.\n" +" --no-slink, -L Do not unpack symbolic links.\n" +" --no-empty-dir, -E Do not unpack directories that would end up\n" +" empty after applying the above rules.\n" +" --no-sparse, -Z Do not create sparse files, always write zero\n" +" blocks to disk.\n" +#ifdef HAVE_SYS_XATTR_H +" --set-xattr, -X When unpacking files to disk, set the extended\n" +" attributes from the squashfs image.\n" +#endif +" --set-times, -T When unpacking files to disk, set the create\n" +" and modify timestamps from the squashfs image.\n" +" --chmod, -C Change permission flags of unpacked files to\n" +" those store in the squashfs image.\n" +" --chown, -O Change ownership of unpacked files to the\n" +" UID/GID set in the squashfs image.\n" +" --quiet, -q Do not print out progress while unpacking.\n" +"\n" +" --help, -h Print help text and exit.\n" +" --version, -V Print version information and exit.\n" +"\n"; + +static char *get_path(char *old, const char *arg) +{ + char *path; + + free(old); + + path = strdup(arg); + if (path == NULL) { + perror("processing arguments"); + exit(EXIT_FAILURE); + } + + if (canonicalize_name(path)) { + fprintf(stderr, "Invalid path: %s\n", arg); + free(path); + exit(EXIT_FAILURE); + } + + return path; +} + +void process_command_line(options_t *opt, int argc, char **argv) +{ + int i; + + opt->op = OP_NONE; + opt->rdtree_flags = 0; + opt->flags = 0; + opt->cmdpath = NULL; + opt->unpack_root = NULL; + opt->image_name = NULL; + + for (;;) { + i = getopt_long(argc, argv, short_opts, long_opts, NULL); + if (i == -1) + break; + + switch (i) { + case 'D': + opt->rdtree_flags |= SQFS_TREE_NO_DEVICES; + break; + case 'S': + opt->rdtree_flags |= SQFS_TREE_NO_SOCKETS; + break; + case 'F': + opt->rdtree_flags |= SQFS_TREE_NO_FIFO; + break; + case 'L': + opt->rdtree_flags |= SQFS_TREE_NO_SLINKS; + break; + case 'E': + opt->rdtree_flags |= SQFS_TREE_NO_EMPTY; + break; + case 'C': + opt->flags |= UNPACK_CHMOD; + break; + case 'O': + opt->flags |= UNPACK_CHOWN; + break; + case 'Z': + opt->flags |= UNPACK_NO_SPARSE; + break; +#ifdef HAVE_SYS_XATTR_H + case 'X': + opt->flags |= UNPACK_SET_XATTR; + break; +#endif + case 'T': + opt->flags |= UNPACK_SET_TIMES; + break; + case 'c': + opt->op = OP_CAT; + opt->cmdpath = get_path(opt->cmdpath, optarg); + break; + case 'd': + opt->op = OP_DESCRIBE; + free(opt->cmdpath); + opt->cmdpath = NULL; + break; + case 'x': + opt->op = OP_RDATTR; + opt->cmdpath = get_path(opt->cmdpath, optarg); + break; + case 's': + opt->op = OP_STAT; + opt->cmdpath = get_path(opt->cmdpath, optarg); + break; + case 'l': + opt->op = OP_LS; + opt->cmdpath = get_path(opt->cmdpath, optarg); + break; + case 'p': + opt->unpack_root = optarg; + break; + case 'u': + opt->op = OP_UNPACK; + opt->cmdpath = get_path(opt->cmdpath, optarg); + break; + case 'q': + opt->flags |= UNPACK_QUIET; + break; + case 'h': + fputs(help_string, stdout); + free(opt->cmdpath); + exit(EXIT_SUCCESS); + case 'V': + print_version("rdsquashfs"); + free(opt->cmdpath); + exit(EXIT_SUCCESS); + default: + goto fail_arg; + } + } + + if (opt->op == OP_NONE) { + fputs("No operation specified\n", stderr); + goto fail_arg; + } + + if (opt->op == OP_LS || opt->op == OP_CAT || opt->op == OP_RDATTR || + opt->op == OP_STAT) { + opt->rdtree_flags |= SQFS_TREE_NO_RECURSE; + } + + if (optind >= argc) { + fputs("Missing image argument\n", stderr); + goto fail_arg; + } + + opt->image_name = argv[optind++]; + return; +fail_arg: + fputs("Try `rdsquashfs --help' for more information.\n", stderr); + free(opt->cmdpath); + exit(EXIT_FAILURE); +} diff --git a/bin/rdsquashfs/src/rdsquashfs.c b/bin/rdsquashfs/src/rdsquashfs.c new file mode 100644 index 0000000..bdcc5a0 --- /dev/null +++ b/bin/rdsquashfs/src/rdsquashfs.c @@ -0,0 +1,275 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * rdsquashfs.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "rdsquashfs.h" + +static sqfs_tree_node_t *list_merge(sqfs_tree_node_t *lhs, + sqfs_tree_node_t *rhs) +{ + sqfs_tree_node_t *it, *head = NULL, **next_ptr = &head; + + while (lhs != NULL && rhs != NULL) { + if (strcmp((const char *)lhs->name, + (const char *)rhs->name) <= 0) { + it = lhs; + lhs = lhs->next; + } else { + it = rhs; + rhs = rhs->next; + } + + *next_ptr = it; + next_ptr = &it->next; + } + + it = (lhs != NULL ? lhs : rhs); + *next_ptr = it; + return head; +} + +static sqfs_tree_node_t *list_sort(sqfs_tree_node_t *head) +{ + sqfs_tree_node_t *it, *half, *prev; + + it = half = prev = head; + + while (it != NULL) { + prev = half; + half = half->next; + it = it->next; + + if (it != NULL) + it = it->next; + } + + if (half == NULL) + return head; + + prev->next = NULL; + + return list_merge(list_sort(head), list_sort(half)); +} + +static int tree_sort(sqfs_tree_node_t *root) +{ + sqfs_tree_node_t *it; + + if (root->children == NULL) + return 0; + + root->children = list_sort(root->children); + + /* + XXX: not only an inconvenience but a security issue: e.g. we unpack a + SquashFS image that has a symlink pointing somewhere, and then a + sub-directory or file with the same name, the unpacker can be tricked + to follow the symlink and write anything, anywhere on the filesystem. + */ + for (it = root->children; it->next != NULL; it = it->next) { + if (strcmp((const char *)it->name, + (const char *)it->next->name) == 0) { + char *path; + int ret; + + ret = sqfs_tree_node_get_path(it, &path); + + if (ret == 0) { + fprintf(stderr, + "Entry '%s' found more than once!\n", + path); + } else { + fputs("Entry found more than once!\n", stderr); + } + + sqfs_free(path); + return -1; + } + } + + for (it = root->children; it != NULL; it = it->next) { + if (tree_sort(it)) + return -1; + } + + return 0; +} + +int main(int argc, char **argv) +{ + sqfs_xattr_reader_t *xattr = NULL; + sqfs_data_reader_t *data = NULL; + sqfs_dir_reader_t *dirrd = NULL; + sqfs_compressor_t *cmp = NULL; + sqfs_id_table_t *idtbl = NULL; + sqfs_compressor_config_t cfg; + sqfs_tree_node_t *n = NULL; + int status = EXIT_FAILURE; + sqfs_file_t *file = NULL; + sqfs_super_t super; + options_t opt; + int ret; + + process_command_line(&opt, argc, argv); + + file = sqfs_open_file(opt.image_name, SQFS_FILE_OPEN_READ_ONLY); + if (file == NULL) { + perror(opt.image_name); + goto out; + } + + ret = sqfs_super_read(&super, file); + if (ret) { + sqfs_perror(opt.image_name, "reading super block", ret); + goto out; + } + + sqfs_compressor_config_init(&cfg, super.compression_id, + super.block_size, + SQFS_COMP_FLAG_UNCOMPRESS); + + ret = sqfs_compressor_create(&cfg, &cmp); + +#ifdef WITH_LZO + if (super.compression_id == SQFS_COMP_LZO && ret != 0) + ret = lzo_compressor_create(&cfg, &cmp); +#endif + + if (ret != 0) { + sqfs_perror(opt.image_name, "creating compressor", ret); + goto out; + } + + if (!(super.flags & SQFS_FLAG_NO_XATTRS)) { + xattr = sqfs_xattr_reader_create(0); + if (xattr == NULL) { + sqfs_perror(opt.image_name, "creating xattr reader", + SQFS_ERROR_ALLOC); + goto out; + } + + ret = sqfs_xattr_reader_load(xattr, &super, file, cmp); + if (ret) { + sqfs_perror(opt.image_name, "loading xattr table", + ret); + goto out; + } + } + + idtbl = sqfs_id_table_create(0); + if (idtbl == NULL) { + sqfs_perror(opt.image_name, "creating ID table", + SQFS_ERROR_ALLOC); + goto out; + } + + ret = sqfs_id_table_read(idtbl, file, &super, cmp); + if (ret) { + sqfs_perror(opt.image_name, "loading ID table", ret); + goto out; + } + + dirrd = sqfs_dir_reader_create(&super, cmp, file, 0); + if (dirrd == NULL) { + sqfs_perror(opt.image_name, "creating dir reader", + SQFS_ERROR_ALLOC); + goto out; + } + + data = sqfs_data_reader_create(file, super.block_size, cmp, 0); + if (data == NULL) { + sqfs_perror(opt.image_name, "creating data reader", + SQFS_ERROR_ALLOC); + goto out; + } + + ret = sqfs_data_reader_load_fragment_table(data, &super); + if (ret) { + sqfs_perror(opt.image_name, "loading fragment table", ret); + goto out; + } + + ret = sqfs_dir_reader_get_full_hierarchy(dirrd, idtbl, opt.cmdpath, + opt.rdtree_flags, &n); + if (ret) { + sqfs_perror(opt.image_name, "reading filesystem tree", ret); + goto out; + } + + switch (opt.op) { + case OP_LS: + list_files(n); + break; + case OP_STAT: + if (stat_file(n)) + goto out; + break; + case OP_CAT: { + ostream_t *fp; + + if (!S_ISREG(n->inode->base.mode)) { + fprintf(stderr, "/%s: not a regular file\n", + opt.cmdpath); + goto out; + } + + fp = ostream_open_stdout(); + if (fp == NULL) + goto out; + + ret = sqfs_data_reader_dump(opt.cmdpath, data, n->inode, + fp, super.block_size); + sqfs_drop(fp); + if (ret) + goto out; + break; + } + case OP_UNPACK: + if (tree_sort(n)) + goto out; + + if (opt.unpack_root != NULL) { + if (mkdir_p(opt.unpack_root)) + goto out; + + if (chdir(opt.unpack_root)) { + perror(opt.unpack_root); + goto out; + } + } + + if (restore_fstree(n, opt.flags)) + goto out; + + if (fill_unpacked_files(super.block_size, n, data, opt.flags)) + goto out; + + if (update_tree_attribs(xattr, n, opt.flags)) + goto out; + break; + case OP_DESCRIBE: + if (describe_tree(n, opt.unpack_root)) + goto out; + break; + case OP_RDATTR: + if (dump_xattrs(xattr, n->inode)) + goto out; + break; + default: + break; + } + + status = EXIT_SUCCESS; +out: + sqfs_dir_tree_destroy(n); + sqfs_drop(data); + sqfs_drop(dirrd); + sqfs_drop(idtbl); + sqfs_drop(xattr); + sqfs_drop(cmp); + sqfs_drop(file); + free(opt.cmdpath); + return status; +} diff --git a/bin/rdsquashfs/src/rdsquashfs.h b/bin/rdsquashfs/src/rdsquashfs.h new file mode 100644 index 0000000..56bb836 --- /dev/null +++ b/bin/rdsquashfs/src/rdsquashfs.h @@ -0,0 +1,82 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * rdsquashfs.h + * + * Copyright (C) 2019 David Oberhollenzer + */ +#ifndef RDSQUASHFS_H +#define RDSQUASHFS_H + +#include "config.h" +#include "common.h" +#include "fstree.h" +#include "util/util.h" + +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#endif +#ifdef HAVE_SYS_XATTR_H +#include + +#if defined(__APPLE__) && defined(__MACH__) +#define lsetxattr(path, name, value, size, flags) \ + setxattr(path, name, value, size, 0, flags | XATTR_NOFOLLOW) +#endif +#endif +#include +#include +#include +#include +#include +#include +#include +#include + +enum UNPACK_FLAGS { + UNPACK_CHMOD = 0x01, + UNPACK_CHOWN = 0x02, + UNPACK_QUIET = 0x04, + UNPACK_NO_SPARSE = 0x08, + UNPACK_SET_XATTR = 0x10, + UNPACK_SET_TIMES = 0x20, +}; + +enum { + OP_NONE = 0, + OP_LS, + OP_CAT, + OP_UNPACK, + OP_DESCRIBE, + OP_RDATTR, + OP_STAT, +}; + +typedef struct { + int op; + int rdtree_flags; + int flags; + char *cmdpath; + const char *unpack_root; + const char *image_name; +} options_t; + +void list_files(const sqfs_tree_node_t *node); + +int stat_file(const sqfs_tree_node_t *node); + +int restore_fstree(sqfs_tree_node_t *root, int flags); + +int update_tree_attribs(sqfs_xattr_reader_t *xattr, + const sqfs_tree_node_t *root, int flags); + +int fill_unpacked_files(size_t blk_sz, const sqfs_tree_node_t *root, + sqfs_data_reader_t *data, int flags); + +int describe_tree(const sqfs_tree_node_t *root, const char *unpack_root); + +int dump_xattrs(sqfs_xattr_reader_t *xattr, const sqfs_inode_generic_t *inode); + +void process_command_line(options_t *opt, int argc, char **argv); + +#endif /* RDSQUASHFS_H */ diff --git a/bin/rdsquashfs/src/restore_fstree.c b/bin/rdsquashfs/src/restore_fstree.c new file mode 100644 index 0000000..ea9d4f1 --- /dev/null +++ b/bin/rdsquashfs/src/restore_fstree.c @@ -0,0 +1,336 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * restore_fstree.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "rdsquashfs.h" + +#ifdef _WIN32 +static int create_node(const sqfs_tree_node_t *n, const char *name, int flags) +{ + WCHAR *wpath; + HANDLE fh; + (void)flags; + + wpath = path_to_windows(name); + if (wpath == NULL) + return -1; + + switch (n->inode->base.mode & S_IFMT) { + case S_IFDIR: + if (!CreateDirectoryW(wpath, NULL)) { + if (GetLastError() != ERROR_ALREADY_EXISTS) + goto fail; + } + break; + case S_IFREG: + fh = CreateFileW(wpath, GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, CREATE_NEW, 0, NULL); + + if (fh == INVALID_HANDLE_VALUE) + goto fail; + + CloseHandle(fh); + break; + default: + break; + } + + free(wpath); + return 0; +fail: { + DWORD err = GetLastError(); + free(wpath); + SetLastError(err); + w32_perror(name); + + if (err == ERROR_FILE_EXISTS) { + fputs("\nHINT: this could be caused by case " + "sensitivity on Windows.\n", stderr); + } + return -1; +} +} +#else +static int create_node(const sqfs_tree_node_t *n, const char *name, int flags) +{ + sqfs_u32 devno; + int fd, mode; + + switch (n->inode->base.mode & S_IFMT) { + case S_IFDIR: + if (mkdir(name, 0755) && errno != EEXIST) { + fprintf(stderr, "mkdir %s: %s\n", + name, strerror(errno)); + return -1; + } + break; + case S_IFLNK: + if (symlink((const char *)n->inode->extra, name)) { + fprintf(stderr, "ln -s %s %s: %s\n", + (const char *)n->inode->extra, name, + strerror(errno)); + return -1; + } + break; + case S_IFSOCK: + case S_IFIFO: + if (mknod(name, (n->inode->base.mode & S_IFMT) | 0700, 0)) { + fprintf(stderr, "creating %s: %s\n", + name, strerror(errno)); + return -1; + } + break; + case S_IFBLK: + case S_IFCHR: + if (n->inode->base.type == SQFS_INODE_EXT_BDEV || + n->inode->base.type == SQFS_INODE_EXT_CDEV) { + devno = n->inode->data.dev_ext.devno; + } else { + devno = n->inode->data.dev.devno; + } + + if (mknod(name, n->inode->base.mode & S_IFMT, devno)) { + fprintf(stderr, "creating device %s: %s\n", + name, strerror(errno)); + return -1; + } + break; + case S_IFREG: + if (flags & UNPACK_CHMOD) { + mode = (n->inode->base.mode & ~S_IFMT) | 0200; + } else { + mode = 0644; + } + + fd = open(name, O_WRONLY | O_CREAT | O_EXCL, mode); + + if (fd < 0) { + fprintf(stderr, "creating %s: %s\n", + name, strerror(errno)); + return -1; + } + + close(fd); + break; + default: + break; + } + + return 0; +} +#endif + +static int create_node_dfs(const sqfs_tree_node_t *n, int flags) +{ + const sqfs_tree_node_t *c; + char *name; + int ret; + + if (!is_filename_sane((const char *)n->name, true)) { + fprintf(stderr, "Found an entry named '%s', skipping.\n", + n->name); + return 0; + } + + ret = sqfs_tree_node_get_path(n, &name); + if (ret != 0) { + sqfs_perror((const char *)n->name, + "constructing full path", ret); + return -1; + } + + ret = canonicalize_name(name); + assert(ret == 0); + + if (!(flags & UNPACK_QUIET)) + printf("creating %s\n", name); + + ret = create_node(n, name, flags); + sqfs_free(name); + if (ret) + return -1; + + if (S_ISDIR(n->inode->base.mode)) { + for (c = n->children; c != NULL; c = c->next) { + if (create_node_dfs(c, flags)) + return -1; + } + } + return 0; +} + +#ifdef HAVE_SYS_XATTR_H +static int set_xattr(const char *path, sqfs_xattr_reader_t *xattr, + const sqfs_tree_node_t *n) +{ + sqfs_xattr_value_t *value; + sqfs_xattr_entry_t *key; + sqfs_xattr_id_t desc; + sqfs_u32 index; + size_t i; + int ret; + + sqfs_inode_get_xattr_index(n->inode, &index); + + if (index == 0xFFFFFFFF) + return 0; + + if (sqfs_xattr_reader_get_desc(xattr, index, &desc)) { + fputs("Error resolving xattr index\n", stderr); + return -1; + } + + if (sqfs_xattr_reader_seek_kv(xattr, &desc)) { + fputs("Error locating xattr key-value pairs\n", stderr); + return -1; + } + + for (i = 0; i < desc.count; ++i) { + if (sqfs_xattr_reader_read_key(xattr, &key)) { + fputs("Error reading xattr key\n", stderr); + return -1; + } + + if (sqfs_xattr_reader_read_value(xattr, key, &value)) { + fputs("Error reading xattr value\n", stderr); + sqfs_free(key); + return -1; + } + + ret = lsetxattr(path, (const char *)key->key, + value->value, value->size, 0); + if (ret) { + fprintf(stderr, "setting xattr '%s' on %s: %s\n", + key->key, path, strerror(errno)); + } + + sqfs_free(key); + sqfs_free(value); + if (ret) + return -1; + } + + return 0; +} +#endif + +static int set_attribs(sqfs_xattr_reader_t *xattr, + const sqfs_tree_node_t *n, int flags) +{ + const sqfs_tree_node_t *c; + char *path; + int ret; + + if (!is_filename_sane((const char *)n->name, true)) + return 0; + + if (S_ISDIR(n->inode->base.mode)) { + for (c = n->children; c != NULL; c = c->next) { + if (set_attribs(xattr, c, flags)) + return -1; + } + } + + ret = sqfs_tree_node_get_path(n, &path); + if (ret != 0) { + sqfs_perror(NULL, "reconstructing full path", ret); + return -1; + } + + ret = canonicalize_name(path); + assert(ret == 0); + +#ifdef HAVE_SYS_XATTR_H + if ((flags & UNPACK_SET_XATTR) && xattr != NULL) { + if (set_xattr(path, xattr, n)) + goto fail; + } +#endif + +#ifndef _WIN32 + if (flags & UNPACK_SET_TIMES) { + struct timespec times[2]; + + memset(times, 0, sizeof(times)); + times[0].tv_sec = n->inode->base.mod_time; + times[1].tv_sec = n->inode->base.mod_time; + + if (utimensat(AT_FDCWD, path, times, AT_SYMLINK_NOFOLLOW)) { + fprintf(stderr, "setting timestamp on %s: %s\n", + path, strerror(errno)); + goto fail; + } + } +#endif + if (flags & UNPACK_CHOWN) { + if (fchownat(AT_FDCWD, path, n->uid, n->gid, + AT_SYMLINK_NOFOLLOW)) { + fprintf(stderr, "chown %s: %s\n", + path, strerror(errno)); + goto fail; + } + } + + if (flags & UNPACK_CHMOD && !S_ISLNK(n->inode->base.mode)) { + if (fchmodat(AT_FDCWD, path, + n->inode->base.mode & ~S_IFMT, 0)) { + fprintf(stderr, "chmod %s: %s\n", + path, strerror(errno)); + goto fail; + } + } + + sqfs_free(path); + return 0; +fail: + sqfs_free(path); + return -1; +} + +int restore_fstree(sqfs_tree_node_t *root, int flags) +{ + sqfs_tree_node_t *n, *old_parent; + + /* make sure fstree_get_path() stops at this node */ + old_parent = root->parent; + root->parent = NULL; + + if (S_ISDIR(root->inode->base.mode)) { + for (n = root->children; n != NULL; n = n->next) { + if (create_node_dfs(n, flags)) + return -1; + } + } else { + if (create_node_dfs(root, flags)) + return -1; + } + + root->parent = old_parent; + return 0; +} + +int update_tree_attribs(sqfs_xattr_reader_t *xattr, + const sqfs_tree_node_t *root, int flags) +{ + const sqfs_tree_node_t *n; + + if ((flags & (UNPACK_CHOWN | UNPACK_CHMOD | + UNPACK_SET_TIMES | UNPACK_SET_XATTR)) == 0) { + return 0; + } + + if (S_ISDIR(root->inode->base.mode)) { + for (n = root->children; n != NULL; n = n->next) { + if (set_attribs(xattr, n, flags)) + return -1; + } + } else { + if (set_attribs(xattr, root, flags)) + return -1; + } + + return 0; +} diff --git a/bin/rdsquashfs/src/stat.c b/bin/rdsquashfs/src/stat.c new file mode 100644 index 0000000..8b4581f --- /dev/null +++ b/bin/rdsquashfs/src/stat.c @@ -0,0 +1,187 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * stat.c + * + * Copyright (C) 2020 David Oberhollenzer + */ +#include "rdsquashfs.h" + +static const char *inode_types[] = { + [SQFS_INODE_DIR] = "directory", + [SQFS_INODE_FILE] = "file", + [SQFS_INODE_SLINK] = "symbolic link", + [SQFS_INODE_BDEV] = "block device", + [SQFS_INODE_CDEV] = "character device", + [SQFS_INODE_FIFO] = "named pipe", + [SQFS_INODE_SOCKET] = "socket", + [SQFS_INODE_EXT_DIR] = "extended directory", + [SQFS_INODE_EXT_FILE] = "extended file", + [SQFS_INODE_EXT_SLINK] = "extended symbolic link", + [SQFS_INODE_EXT_BDEV] = "extended block device", + [SQFS_INODE_EXT_CDEV] = "extended character device", + [SQFS_INODE_EXT_FIFO] = "extended named pipe", + [SQFS_INODE_EXT_SOCKET] = "extended socket", +}; + +int stat_file(const sqfs_tree_node_t *node) +{ + sqfs_u32 xattr_idx = 0xFFFFFFFF, devno = 0, link_size = 0; + const sqfs_inode_generic_t *inode = node->inode; + const char *type = NULL, *link_target = NULL; + sqfs_u32 frag_idx, frag_offset; + bool have_devno = false; + sqfs_u64 location, size; + unsigned int nlinks = 0; + sqfs_dir_index_t *idx; + char buffer[64]; + time_t timeval; + struct tm *tm; + size_t i; + int ret; + + /* decode */ + if ((size_t)inode->base.type < + sizeof(inode_types) / sizeof(inode_types[0])) { + type = inode_types[inode->base.type]; + } + + sqfs_inode_get_xattr_index(inode, &xattr_idx); + + switch (inode->base.type) { + case SQFS_INODE_DIR: + nlinks = inode->data.dir.nlink; + break; + case SQFS_INODE_SLINK: + nlinks = inode->data.slink.nlink; + link_target = (const char *)inode->extra; + link_size = inode->data.slink.target_size; + break; + case SQFS_INODE_BDEV: + case SQFS_INODE_CDEV: + nlinks = inode->data.dev.nlink; + devno = inode->data.dev.devno; + have_devno = true; + break; + case SQFS_INODE_FIFO: + case SQFS_INODE_SOCKET: + nlinks = inode->data.ipc.nlink; + break; + case SQFS_INODE_EXT_DIR: + nlinks = inode->data.dir_ext.nlink; + break; + case SQFS_INODE_EXT_FILE: + nlinks = inode->data.file_ext.nlink; + break; + case SQFS_INODE_EXT_SLINK: + nlinks = inode->data.slink_ext.nlink; + link_target = (const char *)inode->extra; + link_size = inode->data.slink_ext.target_size; + break; + case SQFS_INODE_EXT_BDEV: + case SQFS_INODE_EXT_CDEV: + nlinks = inode->data.dev_ext.nlink; + devno = inode->data.dev_ext.devno; + have_devno = true; + break; + case SQFS_INODE_EXT_FIFO: + case SQFS_INODE_EXT_SOCKET: + nlinks = inode->data.ipc_ext.nlink; + break; + default: + break; + } + + timeval = inode->base.mod_time; + tm = gmtime(&timeval); + strftime(buffer, sizeof(buffer), "%a, %d %b %Y %T %z", tm); + + /* info dump */ + printf("Name: %s\n", (const char *)node->name); + printf("Inode type: %s\n", type == NULL ? "UNKNOWN" : type); + printf("Inode number: %u\n", inode->base.inode_number); + printf("Access: 0%o\n", + (unsigned int)inode->base.mode & ~SQFS_INODE_MODE_MASK); + printf("UID: %u (index = %u)\n", node->uid, inode->base.uid_idx); + printf("GID: %u (index = %u)\n", node->gid, inode->base.gid_idx); + printf("Last modified: %s (%u)\n", buffer, inode->base.mod_time); + + if (type != NULL && inode->base.type != SQFS_INODE_FILE) + printf("Hard link count: %u\n", nlinks); + + if (type != NULL && inode->base.type >= SQFS_INODE_EXT_DIR) + printf("Xattr index: 0x%X\n", xattr_idx); + + if (link_target != NULL) + printf("Link target: %.*s\n", (int)link_size, link_target); + + if (have_devno) { + printf("Device number: %u:%u (%u)\n", + major(devno), minor(devno), devno); + } + + switch (inode->base.type) { + case SQFS_INODE_FILE: + case SQFS_INODE_EXT_FILE: + sqfs_inode_get_file_block_start(inode, &location); + sqfs_inode_get_file_size(inode, &size); + sqfs_inode_get_frag_location(inode, &frag_idx, &frag_offset); + + printf("Fragment index: 0x%X\n", frag_idx); + printf("Fragment offset: %u\n", frag_offset); + printf("File size: %lu\n", (unsigned long)size); + + if (inode->base.type == SQFS_INODE_EXT_FILE) { + printf("Sparse: " PRI_U64 "\n", + inode->data.file_ext.sparse); + } + + printf("Blocks start: %lu\n", (unsigned long)location); + printf("Block count: %lu\n", + (unsigned long)sqfs_inode_get_file_block_count(inode)); + + for (i = 0; i < sqfs_inode_get_file_block_count(inode); ++i) { + printf("\tBlock #%lu size: %u (%s)\n", (unsigned long)i, + SQFS_ON_DISK_BLOCK_SIZE(inode->extra[i]), + SQFS_IS_BLOCK_COMPRESSED(inode->extra[i]) ? + "compressed" : "uncompressed"); + } + break; + case SQFS_INODE_DIR: + printf("Start block: %u\n", inode->data.dir.start_block); + printf("Offset: %u\n", inode->data.dir.offset); + printf("Listing size: %u\n", inode->data.dir.size); + printf("Parent inode: %u\n", inode->data.dir.parent_inode); + break; + case SQFS_INODE_EXT_DIR: + printf("Start block: %u\n", inode->data.dir_ext.start_block); + printf("Offset: %u\n", inode->data.dir_ext.offset); + printf("Listing size: %u\n", inode->data.dir_ext.size); + printf("Parent inode: %u\n", inode->data.dir_ext.parent_inode); + printf("Directory index entries: %u\n", + inode->data.dir_ext.inodex_count); + + if (inode->data.dir_ext.size == 0) + break; + + for (i = 0; ; ++i) { + ret = sqfs_inode_unpack_dir_index_entry(inode, &idx, i); + if (ret == SQFS_ERROR_OUT_OF_BOUNDS) + break; + if (ret < 0) { + sqfs_perror(NULL, "reading directory index", + ret); + return -1; + } + + printf("\t'%.*s' -> block %u, header offset %u\n", + (int)(idx->size + 1), idx->name, + idx->start_block, idx->index); + + sqfs_free(idx); + } + break; + default: + break; + } + return 0; +} diff --git a/bin/rdsquashfs/stat.c b/bin/rdsquashfs/stat.c deleted file mode 100644 index 8b4581f..0000000 --- a/bin/rdsquashfs/stat.c +++ /dev/null @@ -1,187 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * stat.c - * - * Copyright (C) 2020 David Oberhollenzer - */ -#include "rdsquashfs.h" - -static const char *inode_types[] = { - [SQFS_INODE_DIR] = "directory", - [SQFS_INODE_FILE] = "file", - [SQFS_INODE_SLINK] = "symbolic link", - [SQFS_INODE_BDEV] = "block device", - [SQFS_INODE_CDEV] = "character device", - [SQFS_INODE_FIFO] = "named pipe", - [SQFS_INODE_SOCKET] = "socket", - [SQFS_INODE_EXT_DIR] = "extended directory", - [SQFS_INODE_EXT_FILE] = "extended file", - [SQFS_INODE_EXT_SLINK] = "extended symbolic link", - [SQFS_INODE_EXT_BDEV] = "extended block device", - [SQFS_INODE_EXT_CDEV] = "extended character device", - [SQFS_INODE_EXT_FIFO] = "extended named pipe", - [SQFS_INODE_EXT_SOCKET] = "extended socket", -}; - -int stat_file(const sqfs_tree_node_t *node) -{ - sqfs_u32 xattr_idx = 0xFFFFFFFF, devno = 0, link_size = 0; - const sqfs_inode_generic_t *inode = node->inode; - const char *type = NULL, *link_target = NULL; - sqfs_u32 frag_idx, frag_offset; - bool have_devno = false; - sqfs_u64 location, size; - unsigned int nlinks = 0; - sqfs_dir_index_t *idx; - char buffer[64]; - time_t timeval; - struct tm *tm; - size_t i; - int ret; - - /* decode */ - if ((size_t)inode->base.type < - sizeof(inode_types) / sizeof(inode_types[0])) { - type = inode_types[inode->base.type]; - } - - sqfs_inode_get_xattr_index(inode, &xattr_idx); - - switch (inode->base.type) { - case SQFS_INODE_DIR: - nlinks = inode->data.dir.nlink; - break; - case SQFS_INODE_SLINK: - nlinks = inode->data.slink.nlink; - link_target = (const char *)inode->extra; - link_size = inode->data.slink.target_size; - break; - case SQFS_INODE_BDEV: - case SQFS_INODE_CDEV: - nlinks = inode->data.dev.nlink; - devno = inode->data.dev.devno; - have_devno = true; - break; - case SQFS_INODE_FIFO: - case SQFS_INODE_SOCKET: - nlinks = inode->data.ipc.nlink; - break; - case SQFS_INODE_EXT_DIR: - nlinks = inode->data.dir_ext.nlink; - break; - case SQFS_INODE_EXT_FILE: - nlinks = inode->data.file_ext.nlink; - break; - case SQFS_INODE_EXT_SLINK: - nlinks = inode->data.slink_ext.nlink; - link_target = (const char *)inode->extra; - link_size = inode->data.slink_ext.target_size; - break; - case SQFS_INODE_EXT_BDEV: - case SQFS_INODE_EXT_CDEV: - nlinks = inode->data.dev_ext.nlink; - devno = inode->data.dev_ext.devno; - have_devno = true; - break; - case SQFS_INODE_EXT_FIFO: - case SQFS_INODE_EXT_SOCKET: - nlinks = inode->data.ipc_ext.nlink; - break; - default: - break; - } - - timeval = inode->base.mod_time; - tm = gmtime(&timeval); - strftime(buffer, sizeof(buffer), "%a, %d %b %Y %T %z", tm); - - /* info dump */ - printf("Name: %s\n", (const char *)node->name); - printf("Inode type: %s\n", type == NULL ? "UNKNOWN" : type); - printf("Inode number: %u\n", inode->base.inode_number); - printf("Access: 0%o\n", - (unsigned int)inode->base.mode & ~SQFS_INODE_MODE_MASK); - printf("UID: %u (index = %u)\n", node->uid, inode->base.uid_idx); - printf("GID: %u (index = %u)\n", node->gid, inode->base.gid_idx); - printf("Last modified: %s (%u)\n", buffer, inode->base.mod_time); - - if (type != NULL && inode->base.type != SQFS_INODE_FILE) - printf("Hard link count: %u\n", nlinks); - - if (type != NULL && inode->base.type >= SQFS_INODE_EXT_DIR) - printf("Xattr index: 0x%X\n", xattr_idx); - - if (link_target != NULL) - printf("Link target: %.*s\n", (int)link_size, link_target); - - if (have_devno) { - printf("Device number: %u:%u (%u)\n", - major(devno), minor(devno), devno); - } - - switch (inode->base.type) { - case SQFS_INODE_FILE: - case SQFS_INODE_EXT_FILE: - sqfs_inode_get_file_block_start(inode, &location); - sqfs_inode_get_file_size(inode, &size); - sqfs_inode_get_frag_location(inode, &frag_idx, &frag_offset); - - printf("Fragment index: 0x%X\n", frag_idx); - printf("Fragment offset: %u\n", frag_offset); - printf("File size: %lu\n", (unsigned long)size); - - if (inode->base.type == SQFS_INODE_EXT_FILE) { - printf("Sparse: " PRI_U64 "\n", - inode->data.file_ext.sparse); - } - - printf("Blocks start: %lu\n", (unsigned long)location); - printf("Block count: %lu\n", - (unsigned long)sqfs_inode_get_file_block_count(inode)); - - for (i = 0; i < sqfs_inode_get_file_block_count(inode); ++i) { - printf("\tBlock #%lu size: %u (%s)\n", (unsigned long)i, - SQFS_ON_DISK_BLOCK_SIZE(inode->extra[i]), - SQFS_IS_BLOCK_COMPRESSED(inode->extra[i]) ? - "compressed" : "uncompressed"); - } - break; - case SQFS_INODE_DIR: - printf("Start block: %u\n", inode->data.dir.start_block); - printf("Offset: %u\n", inode->data.dir.offset); - printf("Listing size: %u\n", inode->data.dir.size); - printf("Parent inode: %u\n", inode->data.dir.parent_inode); - break; - case SQFS_INODE_EXT_DIR: - printf("Start block: %u\n", inode->data.dir_ext.start_block); - printf("Offset: %u\n", inode->data.dir_ext.offset); - printf("Listing size: %u\n", inode->data.dir_ext.size); - printf("Parent inode: %u\n", inode->data.dir_ext.parent_inode); - printf("Directory index entries: %u\n", - inode->data.dir_ext.inodex_count); - - if (inode->data.dir_ext.size == 0) - break; - - for (i = 0; ; ++i) { - ret = sqfs_inode_unpack_dir_index_entry(inode, &idx, i); - if (ret == SQFS_ERROR_OUT_OF_BOUNDS) - break; - if (ret < 0) { - sqfs_perror(NULL, "reading directory index", - ret); - return -1; - } - - printf("\t'%.*s' -> block %u, header offset %u\n", - (int)(idx->size + 1), idx->name, - idx->start_block, idx->index); - - sqfs_free(idx); - } - break; - default: - break; - } - return 0; -} diff --git a/bin/sqfs2tar/Makemodule.am b/bin/sqfs2tar/Makemodule.am index 05cee5b..2e6c411 100644 --- a/bin/sqfs2tar/Makemodule.am +++ b/bin/sqfs2tar/Makemodule.am @@ -1,6 +1,6 @@ -sqfs2tar_SOURCES = bin/sqfs2tar/sqfs2tar.c bin/sqfs2tar/sqfs2tar.h -sqfs2tar_SOURCES += bin/sqfs2tar/options.c bin/sqfs2tar/write_tree.c -sqfs2tar_SOURCES += bin/sqfs2tar/xattr.c +sqfs2tar_SOURCES = bin/sqfs2tar/src/sqfs2tar.c bin/sqfs2tar/src/sqfs2tar.h \ + bin/sqfs2tar/src/options.c bin/sqfs2tar/src/write_tree.c \ + bin/sqfs2tar/src/xattr.c sqfs2tar_CFLAGS = $(AM_CFLAGS) $(PTHREAD_CFLAGS) sqfs2tar_LDADD = libcommon.a libutil.a libsquashfs.la libtar.a sqfs2tar_LDADD += libio.a libxfrm.a libcompat.a libfstree.a diff --git a/bin/sqfs2tar/options.c b/bin/sqfs2tar/options.c deleted file mode 100644 index ba1588d..0000000 --- a/bin/sqfs2tar/options.c +++ /dev/null @@ -1,212 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * options.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "sqfs2tar.h" - -static struct option long_opts[] = { - { "compressor", required_argument, NULL, 'c' }, - { "subdir", required_argument, NULL, 'd' }, - { "keep-as-dir", no_argument, NULL, 'k' }, - { "root-becomes", required_argument, NULL, 'r' }, - { "no-skip", no_argument, NULL, 's' }, - { "no-xattr", no_argument, NULL, 'X' }, - { "no-hard-links", no_argument, NULL, 'L' }, - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, 'V' }, - { NULL, 0, NULL, 0 }, -}; - -static const char *short_opts = "c:d:kr:sXLhV"; - -static const char *usagestr = -"Usage: sqfs2tar [OPTIONS...] \n" -"\n" -"Read an input squashfs archive and turn it into a tar archive, written\n" -"to stdout.\n" -"\n" -"Possible options:\n" -"\n" -" --compressor, -c If set, stream compress the resulting tarball.\n" -" By default, the tarball is uncompressed.\n" -"\n" -" --subdir, -d Unpack the given sub directory instead of the\n" -" filesystem root. Can be specified more than\n" -" once to select multiple directories. If only\n" -" one is specified, it becomes the new root of\n" -" node of the archive file system tree.\n" -"\n" -" --root-becomes, -r Turn the root inode into a directory with the\n" -" specified name. Everything else will be stored\n" -" inside this directory. The special value '.' is\n" -" allowed to prefix all tar paths with './' and\n" -" add an entry named '.' for the root inode.\n" -" If this option isn't used, all meta data stored\n" -" in the root inode IS LOST!\n" -"\n" -" --keep-as-dir, -k If --subdir is used only once, don't make the\n" -" subdir the archive root, instead keep it as\n" -" prefix for all unpacked files.\n" -" Using --subdir more than once implies\n" -" --keep-as-dir.\n" -" --no-xattr, -X Do not copy extended attributes.\n" -" --no-hard-links, -L Do not generate hard links. Produce duplicate\n" -" entries instead.\n" -"\n" -" --no-skip, -s Abort if a file cannot be stored in a tar\n" -" archive. By default, it is simply skipped\n" -" and a warning is written to stderr.\n" -"\n" -" --help, -h Print help text and exit.\n" -" --version, -V Print version information and exit.\n" -"\n" -"Supported tar compression formats:\n"; - -bool dont_skip = false; -bool keep_as_dir = false; -bool no_xattr = false; -bool no_links = false; - -char *root_becomes = NULL; -char **subdirs = NULL; -size_t num_subdirs = 0; -static size_t max_subdirs = 0; -int compressor = 0; - -const char *filename = NULL; - -void process_args(int argc, char **argv) -{ - size_t idx, new_count; - const char *name; - int i, ret; - char **new; - - for (;;) { - i = getopt_long(argc, argv, short_opts, long_opts, NULL); - if (i == -1) - break; - - switch (i) { - case 'c': - compressor = xfrm_compressor_id_from_name(optarg); - if (compressor <= 0) { - fprintf(stderr, "unknown compressor '%s'.\n", - optarg); - goto fail; - } - break; - case 'd': - if (num_subdirs == max_subdirs) { - new_count = max_subdirs ? max_subdirs * 2 : 16; - new = realloc(subdirs, - new_count * sizeof(subdirs[0])); - if (new == NULL) - goto fail_errno; - - max_subdirs = new_count; - subdirs = new; - } - - subdirs[num_subdirs] = strdup(optarg); - if (subdirs[num_subdirs] == NULL) - goto fail_errno; - - if (canonicalize_name(subdirs[num_subdirs])) { - perror(optarg); - goto fail; - } - - ++num_subdirs; - break; - case 'r': - free(root_becomes); - root_becomes = strdup(optarg); - if (root_becomes == NULL) - goto fail_errno; - - if (strcmp(root_becomes, "./") == 0) - root_becomes[1] = '\0'; - - if (strcmp(root_becomes, ".") == 0) - break; - - if (canonicalize_name(root_becomes) != 0 || - strlen(root_becomes) == 0) { - fprintf(stderr, - "Invalid root directory '%s'.\n", - optarg); - goto fail_arg; - } - break; - case 'k': - keep_as_dir = true; - break; - case 's': - dont_skip = true; - break; - case 'X': - no_xattr = true; - break; - case 'L': - no_links = true; - break; - case 'h': - fputs(usagestr, stdout); - - i = XFRM_COMPRESSOR_MIN; - - while (i <= XFRM_COMPRESSOR_MAX) { - name = xfrm_compressor_name_from_id(i); - if (name != NULL) - printf("\t%s\n", name); - ++i; - } - - fputc('\n', stdout); - goto out_success; - case 'V': - print_version("sqfs2tar"); - goto out_success; - default: - goto fail_arg; - } - } - - if (optind >= argc) { - fputs("Missing argument: squashfs image\n", stderr); - goto fail_arg; - } - - filename = argv[optind++]; - - if (optind < argc) { - fputs("Unknown extra arguments\n", stderr); - goto fail_arg; - } - - if (num_subdirs > 1) - keep_as_dir = true; - - return; -fail_errno: - perror("parsing options"); - goto fail; -fail_arg: - fputs("Try `sqfs2tar --help' for more information.\n", stderr); - goto fail; -fail: - ret = EXIT_FAILURE; - goto out_exit; -out_success: - ret = EXIT_SUCCESS; - goto out_exit; -out_exit: - for (idx = 0; idx < num_subdirs; ++idx) - free(subdirs[idx]); - free(root_becomes); - free(subdirs); - exit(ret); -} diff --git a/bin/sqfs2tar/sqfs2tar.c b/bin/sqfs2tar/sqfs2tar.c deleted file mode 100644 index 43f9e78..0000000 --- a/bin/sqfs2tar/sqfs2tar.c +++ /dev/null @@ -1,274 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * sqfs2tar.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "sqfs2tar.h" - -sqfs_xattr_reader_t *xr; -sqfs_data_reader_t *data; -sqfs_super_t super; -ostream_t *out_file = NULL; - -static sqfs_file_t *file; - -char *assemble_tar_path(char *name, bool is_dir) -{ - size_t len, new_len; - char *temp; - (void)is_dir; - - if (root_becomes == NULL && !is_dir) - return name; - - new_len = strlen(name); - if (root_becomes != NULL) - new_len += strlen(root_becomes) + 1; - if (is_dir) - new_len += 1; - - temp = realloc(name, new_len + 1); - if (temp == NULL) { - perror("assembling tar entry filename"); - free(name); - return NULL; - } - - name = temp; - - if (root_becomes != NULL) { - len = strlen(root_becomes); - - memmove(name + len + 1, name, strlen(name) + 1); - memcpy(name, root_becomes, len); - name[len] = '/'; - } - - if (is_dir) { - len = strlen(name); - - if (len == 0 || name[len - 1] != '/') { - name[len++] = '/'; - name[len] = '\0'; - } - } - - return name; -} - -static int terminate_archive(void) -{ - char buffer[1024]; - - memset(buffer, '\0', sizeof(buffer)); - - return ostream_append(out_file, buffer, sizeof(buffer)); -} - -static sqfs_tree_node_t *tree_merge(sqfs_tree_node_t *lhs, - sqfs_tree_node_t *rhs) -{ - sqfs_tree_node_t *head = NULL, **next_ptr = &head; - sqfs_tree_node_t *it, *l, *r; - int diff; - - while (lhs->children != NULL && rhs->children != NULL) { - diff = strcmp((const char *)lhs->children->name, - (const char *)rhs->children->name); - - if (diff < 0) { - it = lhs->children; - lhs->children = lhs->children->next; - } else if (diff > 0) { - it = rhs->children; - rhs->children = rhs->children->next; - } else { - l = lhs->children; - lhs->children = lhs->children->next; - - r = rhs->children; - rhs->children = rhs->children->next; - - it = tree_merge(l, r); - } - - *next_ptr = it; - next_ptr = &it->next; - } - - it = (lhs->children != NULL ? lhs->children : rhs->children); - *next_ptr = it; - - sqfs_dir_tree_destroy(rhs); - lhs->children = head; - return lhs; -} - -int main(int argc, char **argv) -{ - sqfs_tree_node_t *root = NULL, *subtree = NULL; - int flags, ret, status = EXIT_FAILURE; - sqfs_compressor_t *cmp = NULL; - sqfs_id_table_t *idtbl = NULL; - sqfs_dir_reader_t *dr = NULL; - sqfs_compressor_config_t cfg; - size_t i; - - process_args(argc, argv); - - out_file = ostream_open_stdout(); - if (out_file == NULL) { - perror("changing stdout to binary mode"); - goto out; - } - - if (compressor > 0) { - xfrm_stream_t *xfrm = compressor_stream_create(compressor,NULL); - ostream_t *strm; - - if (xfrm == NULL) - goto out; - - strm = ostream_xfrm_create(out_file, xfrm); - sqfs_drop(out_file); - sqfs_drop(xfrm); - out_file = strm; - - if (out_file == NULL) - goto out; - } - - file = sqfs_open_file(filename, SQFS_FILE_OPEN_READ_ONLY); - if (file == NULL) { - perror(filename); - goto out; - } - - ret = sqfs_super_read(&super, file); - if (ret) { - sqfs_perror(filename, "reading super block", ret); - goto out; - } - - sqfs_compressor_config_init(&cfg, super.compression_id, - super.block_size, - SQFS_COMP_FLAG_UNCOMPRESS); - - ret = sqfs_compressor_create(&cfg, &cmp); - -#ifdef WITH_LZO - if (super.compression_id == SQFS_COMP_LZO && ret != 0) - ret = lzo_compressor_create(&cfg, &cmp); -#endif - - if (ret != 0) { - sqfs_perror(filename, "creating compressor", ret); - goto out; - } - - idtbl = sqfs_id_table_create(0); - - if (idtbl == NULL) { - perror("creating ID table"); - goto out; - } - - ret = sqfs_id_table_read(idtbl, file, &super, cmp); - if (ret) { - sqfs_perror(filename, "loading ID table", ret); - goto out; - } - - data = sqfs_data_reader_create(file, super.block_size, cmp, 0); - if (data == NULL) { - sqfs_perror(filename, "creating data reader", - SQFS_ERROR_ALLOC); - goto out; - } - - ret = sqfs_data_reader_load_fragment_table(data, &super); - if (ret) { - sqfs_perror(filename, "loading fragment table", ret); - goto out; - } - - dr = sqfs_dir_reader_create(&super, cmp, file, 0); - if (dr == NULL) { - sqfs_perror(filename, "creating dir reader", - SQFS_ERROR_ALLOC); - goto out; - } - - if (!no_xattr && !(super.flags & SQFS_FLAG_NO_XATTRS)) { - xr = sqfs_xattr_reader_create(0); - if (xr == NULL) { - sqfs_perror(filename, "creating xattr reader", - SQFS_ERROR_ALLOC); - goto out; - } - - ret = sqfs_xattr_reader_load(xr, &super, file, cmp); - if (ret) { - sqfs_perror(filename, "loading xattr table", ret); - goto out; - } - } - - if (num_subdirs == 0) { - ret = sqfs_dir_reader_get_full_hierarchy(dr, idtbl, NULL, - 0, &root); - if (ret) { - sqfs_perror(filename, "loading filesystem tree", ret); - goto out; - } - } else { - flags = 0; - - if (keep_as_dir || num_subdirs > 1) - flags = SQFS_TREE_STORE_PARENTS; - - for (i = 0; i < num_subdirs; ++i) { - ret = sqfs_dir_reader_get_full_hierarchy(dr, idtbl, - subdirs[i], - flags, - &subtree); - if (ret) { - sqfs_perror(subdirs[i], "loading filesystem " - "tree", ret); - goto out; - } - - if (root == NULL) { - root = subtree; - } else { - root = tree_merge(root, subtree); - } - } - } - - if (write_tree(root)) - goto out; - - if (terminate_archive()) - goto out; - - if (ostream_flush(out_file)) - goto out; - - status = EXIT_SUCCESS; -out: - sqfs_dir_tree_destroy(root); - sqfs_drop(xr); - sqfs_drop(dr); - sqfs_drop(data); - sqfs_drop(idtbl); - sqfs_drop(cmp); - sqfs_drop(file); - sqfs_drop(out_file); - for (i = 0; i < num_subdirs; ++i) - free(subdirs[i]); - free(subdirs); - free(root_becomes); - return status; -} diff --git a/bin/sqfs2tar/sqfs2tar.h b/bin/sqfs2tar/sqfs2tar.h deleted file mode 100644 index 4bf5428..0000000 --- a/bin/sqfs2tar/sqfs2tar.h +++ /dev/null @@ -1,56 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * sqfs2tar.h - * - * Copyright (C) 2019 David Oberhollenzer - */ -#ifndef SQFS2TAR_H -#define SQFS2TAR_H - -#include "config.h" -#include "common.h" - -#include "util/util.h" -#include "tar/tar.h" -#include "xfrm/compress.h" -#include "io/xfrm.h" - -#include -#include -#include -#include -#include -#include -#include - -/* options.c */ -extern bool dont_skip; -extern bool keep_as_dir; -extern bool no_xattr; -extern bool no_links; - -extern char *root_becomes; -extern char **subdirs; -extern size_t num_subdirs; -extern int compressor; - -extern const char *filename; - -void process_args(int argc, char **argv); - -/* tar2sqfs.c */ -extern sqfs_xattr_reader_t *xr; -extern sqfs_data_reader_t *data; -extern sqfs_super_t super; -extern ostream_t *out_file; - -char *assemble_tar_path(char *name, bool is_dir); - -/* xattr.c */ -int get_xattrs(const char *name, const sqfs_inode_generic_t *inode, - tar_xattr_t **out); - -/* write_tree.c */ -int write_tree(const sqfs_tree_node_t *n); - -#endif /* SQFS2TAR_H */ diff --git a/bin/sqfs2tar/src/options.c b/bin/sqfs2tar/src/options.c new file mode 100644 index 0000000..ba1588d --- /dev/null +++ b/bin/sqfs2tar/src/options.c @@ -0,0 +1,212 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * options.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "sqfs2tar.h" + +static struct option long_opts[] = { + { "compressor", required_argument, NULL, 'c' }, + { "subdir", required_argument, NULL, 'd' }, + { "keep-as-dir", no_argument, NULL, 'k' }, + { "root-becomes", required_argument, NULL, 'r' }, + { "no-skip", no_argument, NULL, 's' }, + { "no-xattr", no_argument, NULL, 'X' }, + { "no-hard-links", no_argument, NULL, 'L' }, + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, 'V' }, + { NULL, 0, NULL, 0 }, +}; + +static const char *short_opts = "c:d:kr:sXLhV"; + +static const char *usagestr = +"Usage: sqfs2tar [OPTIONS...] \n" +"\n" +"Read an input squashfs archive and turn it into a tar archive, written\n" +"to stdout.\n" +"\n" +"Possible options:\n" +"\n" +" --compressor, -c If set, stream compress the resulting tarball.\n" +" By default, the tarball is uncompressed.\n" +"\n" +" --subdir, -d Unpack the given sub directory instead of the\n" +" filesystem root. Can be specified more than\n" +" once to select multiple directories. If only\n" +" one is specified, it becomes the new root of\n" +" node of the archive file system tree.\n" +"\n" +" --root-becomes, -r Turn the root inode into a directory with the\n" +" specified name. Everything else will be stored\n" +" inside this directory. The special value '.' is\n" +" allowed to prefix all tar paths with './' and\n" +" add an entry named '.' for the root inode.\n" +" If this option isn't used, all meta data stored\n" +" in the root inode IS LOST!\n" +"\n" +" --keep-as-dir, -k If --subdir is used only once, don't make the\n" +" subdir the archive root, instead keep it as\n" +" prefix for all unpacked files.\n" +" Using --subdir more than once implies\n" +" --keep-as-dir.\n" +" --no-xattr, -X Do not copy extended attributes.\n" +" --no-hard-links, -L Do not generate hard links. Produce duplicate\n" +" entries instead.\n" +"\n" +" --no-skip, -s Abort if a file cannot be stored in a tar\n" +" archive. By default, it is simply skipped\n" +" and a warning is written to stderr.\n" +"\n" +" --help, -h Print help text and exit.\n" +" --version, -V Print version information and exit.\n" +"\n" +"Supported tar compression formats:\n"; + +bool dont_skip = false; +bool keep_as_dir = false; +bool no_xattr = false; +bool no_links = false; + +char *root_becomes = NULL; +char **subdirs = NULL; +size_t num_subdirs = 0; +static size_t max_subdirs = 0; +int compressor = 0; + +const char *filename = NULL; + +void process_args(int argc, char **argv) +{ + size_t idx, new_count; + const char *name; + int i, ret; + char **new; + + for (;;) { + i = getopt_long(argc, argv, short_opts, long_opts, NULL); + if (i == -1) + break; + + switch (i) { + case 'c': + compressor = xfrm_compressor_id_from_name(optarg); + if (compressor <= 0) { + fprintf(stderr, "unknown compressor '%s'.\n", + optarg); + goto fail; + } + break; + case 'd': + if (num_subdirs == max_subdirs) { + new_count = max_subdirs ? max_subdirs * 2 : 16; + new = realloc(subdirs, + new_count * sizeof(subdirs[0])); + if (new == NULL) + goto fail_errno; + + max_subdirs = new_count; + subdirs = new; + } + + subdirs[num_subdirs] = strdup(optarg); + if (subdirs[num_subdirs] == NULL) + goto fail_errno; + + if (canonicalize_name(subdirs[num_subdirs])) { + perror(optarg); + goto fail; + } + + ++num_subdirs; + break; + case 'r': + free(root_becomes); + root_becomes = strdup(optarg); + if (root_becomes == NULL) + goto fail_errno; + + if (strcmp(root_becomes, "./") == 0) + root_becomes[1] = '\0'; + + if (strcmp(root_becomes, ".") == 0) + break; + + if (canonicalize_name(root_becomes) != 0 || + strlen(root_becomes) == 0) { + fprintf(stderr, + "Invalid root directory '%s'.\n", + optarg); + goto fail_arg; + } + break; + case 'k': + keep_as_dir = true; + break; + case 's': + dont_skip = true; + break; + case 'X': + no_xattr = true; + break; + case 'L': + no_links = true; + break; + case 'h': + fputs(usagestr, stdout); + + i = XFRM_COMPRESSOR_MIN; + + while (i <= XFRM_COMPRESSOR_MAX) { + name = xfrm_compressor_name_from_id(i); + if (name != NULL) + printf("\t%s\n", name); + ++i; + } + + fputc('\n', stdout); + goto out_success; + case 'V': + print_version("sqfs2tar"); + goto out_success; + default: + goto fail_arg; + } + } + + if (optind >= argc) { + fputs("Missing argument: squashfs image\n", stderr); + goto fail_arg; + } + + filename = argv[optind++]; + + if (optind < argc) { + fputs("Unknown extra arguments\n", stderr); + goto fail_arg; + } + + if (num_subdirs > 1) + keep_as_dir = true; + + return; +fail_errno: + perror("parsing options"); + goto fail; +fail_arg: + fputs("Try `sqfs2tar --help' for more information.\n", stderr); + goto fail; +fail: + ret = EXIT_FAILURE; + goto out_exit; +out_success: + ret = EXIT_SUCCESS; + goto out_exit; +out_exit: + for (idx = 0; idx < num_subdirs; ++idx) + free(subdirs[idx]); + free(root_becomes); + free(subdirs); + exit(ret); +} diff --git a/bin/sqfs2tar/src/sqfs2tar.c b/bin/sqfs2tar/src/sqfs2tar.c new file mode 100644 index 0000000..43f9e78 --- /dev/null +++ b/bin/sqfs2tar/src/sqfs2tar.c @@ -0,0 +1,274 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * sqfs2tar.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "sqfs2tar.h" + +sqfs_xattr_reader_t *xr; +sqfs_data_reader_t *data; +sqfs_super_t super; +ostream_t *out_file = NULL; + +static sqfs_file_t *file; + +char *assemble_tar_path(char *name, bool is_dir) +{ + size_t len, new_len; + char *temp; + (void)is_dir; + + if (root_becomes == NULL && !is_dir) + return name; + + new_len = strlen(name); + if (root_becomes != NULL) + new_len += strlen(root_becomes) + 1; + if (is_dir) + new_len += 1; + + temp = realloc(name, new_len + 1); + if (temp == NULL) { + perror("assembling tar entry filename"); + free(name); + return NULL; + } + + name = temp; + + if (root_becomes != NULL) { + len = strlen(root_becomes); + + memmove(name + len + 1, name, strlen(name) + 1); + memcpy(name, root_becomes, len); + name[len] = '/'; + } + + if (is_dir) { + len = strlen(name); + + if (len == 0 || name[len - 1] != '/') { + name[len++] = '/'; + name[len] = '\0'; + } + } + + return name; +} + +static int terminate_archive(void) +{ + char buffer[1024]; + + memset(buffer, '\0', sizeof(buffer)); + + return ostream_append(out_file, buffer, sizeof(buffer)); +} + +static sqfs_tree_node_t *tree_merge(sqfs_tree_node_t *lhs, + sqfs_tree_node_t *rhs) +{ + sqfs_tree_node_t *head = NULL, **next_ptr = &head; + sqfs_tree_node_t *it, *l, *r; + int diff; + + while (lhs->children != NULL && rhs->children != NULL) { + diff = strcmp((const char *)lhs->children->name, + (const char *)rhs->children->name); + + if (diff < 0) { + it = lhs->children; + lhs->children = lhs->children->next; + } else if (diff > 0) { + it = rhs->children; + rhs->children = rhs->children->next; + } else { + l = lhs->children; + lhs->children = lhs->children->next; + + r = rhs->children; + rhs->children = rhs->children->next; + + it = tree_merge(l, r); + } + + *next_ptr = it; + next_ptr = &it->next; + } + + it = (lhs->children != NULL ? lhs->children : rhs->children); + *next_ptr = it; + + sqfs_dir_tree_destroy(rhs); + lhs->children = head; + return lhs; +} + +int main(int argc, char **argv) +{ + sqfs_tree_node_t *root = NULL, *subtree = NULL; + int flags, ret, status = EXIT_FAILURE; + sqfs_compressor_t *cmp = NULL; + sqfs_id_table_t *idtbl = NULL; + sqfs_dir_reader_t *dr = NULL; + sqfs_compressor_config_t cfg; + size_t i; + + process_args(argc, argv); + + out_file = ostream_open_stdout(); + if (out_file == NULL) { + perror("changing stdout to binary mode"); + goto out; + } + + if (compressor > 0) { + xfrm_stream_t *xfrm = compressor_stream_create(compressor,NULL); + ostream_t *strm; + + if (xfrm == NULL) + goto out; + + strm = ostream_xfrm_create(out_file, xfrm); + sqfs_drop(out_file); + sqfs_drop(xfrm); + out_file = strm; + + if (out_file == NULL) + goto out; + } + + file = sqfs_open_file(filename, SQFS_FILE_OPEN_READ_ONLY); + if (file == NULL) { + perror(filename); + goto out; + } + + ret = sqfs_super_read(&super, file); + if (ret) { + sqfs_perror(filename, "reading super block", ret); + goto out; + } + + sqfs_compressor_config_init(&cfg, super.compression_id, + super.block_size, + SQFS_COMP_FLAG_UNCOMPRESS); + + ret = sqfs_compressor_create(&cfg, &cmp); + +#ifdef WITH_LZO + if (super.compression_id == SQFS_COMP_LZO && ret != 0) + ret = lzo_compressor_create(&cfg, &cmp); +#endif + + if (ret != 0) { + sqfs_perror(filename, "creating compressor", ret); + goto out; + } + + idtbl = sqfs_id_table_create(0); + + if (idtbl == NULL) { + perror("creating ID table"); + goto out; + } + + ret = sqfs_id_table_read(idtbl, file, &super, cmp); + if (ret) { + sqfs_perror(filename, "loading ID table", ret); + goto out; + } + + data = sqfs_data_reader_create(file, super.block_size, cmp, 0); + if (data == NULL) { + sqfs_perror(filename, "creating data reader", + SQFS_ERROR_ALLOC); + goto out; + } + + ret = sqfs_data_reader_load_fragment_table(data, &super); + if (ret) { + sqfs_perror(filename, "loading fragment table", ret); + goto out; + } + + dr = sqfs_dir_reader_create(&super, cmp, file, 0); + if (dr == NULL) { + sqfs_perror(filename, "creating dir reader", + SQFS_ERROR_ALLOC); + goto out; + } + + if (!no_xattr && !(super.flags & SQFS_FLAG_NO_XATTRS)) { + xr = sqfs_xattr_reader_create(0); + if (xr == NULL) { + sqfs_perror(filename, "creating xattr reader", + SQFS_ERROR_ALLOC); + goto out; + } + + ret = sqfs_xattr_reader_load(xr, &super, file, cmp); + if (ret) { + sqfs_perror(filename, "loading xattr table", ret); + goto out; + } + } + + if (num_subdirs == 0) { + ret = sqfs_dir_reader_get_full_hierarchy(dr, idtbl, NULL, + 0, &root); + if (ret) { + sqfs_perror(filename, "loading filesystem tree", ret); + goto out; + } + } else { + flags = 0; + + if (keep_as_dir || num_subdirs > 1) + flags = SQFS_TREE_STORE_PARENTS; + + for (i = 0; i < num_subdirs; ++i) { + ret = sqfs_dir_reader_get_full_hierarchy(dr, idtbl, + subdirs[i], + flags, + &subtree); + if (ret) { + sqfs_perror(subdirs[i], "loading filesystem " + "tree", ret); + goto out; + } + + if (root == NULL) { + root = subtree; + } else { + root = tree_merge(root, subtree); + } + } + } + + if (write_tree(root)) + goto out; + + if (terminate_archive()) + goto out; + + if (ostream_flush(out_file)) + goto out; + + status = EXIT_SUCCESS; +out: + sqfs_dir_tree_destroy(root); + sqfs_drop(xr); + sqfs_drop(dr); + sqfs_drop(data); + sqfs_drop(idtbl); + sqfs_drop(cmp); + sqfs_drop(file); + sqfs_drop(out_file); + for (i = 0; i < num_subdirs; ++i) + free(subdirs[i]); + free(subdirs); + free(root_becomes); + return status; +} diff --git a/bin/sqfs2tar/src/sqfs2tar.h b/bin/sqfs2tar/src/sqfs2tar.h new file mode 100644 index 0000000..4bf5428 --- /dev/null +++ b/bin/sqfs2tar/src/sqfs2tar.h @@ -0,0 +1,56 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * sqfs2tar.h + * + * Copyright (C) 2019 David Oberhollenzer + */ +#ifndef SQFS2TAR_H +#define SQFS2TAR_H + +#include "config.h" +#include "common.h" + +#include "util/util.h" +#include "tar/tar.h" +#include "xfrm/compress.h" +#include "io/xfrm.h" + +#include +#include +#include +#include +#include +#include +#include + +/* options.c */ +extern bool dont_skip; +extern bool keep_as_dir; +extern bool no_xattr; +extern bool no_links; + +extern char *root_becomes; +extern char **subdirs; +extern size_t num_subdirs; +extern int compressor; + +extern const char *filename; + +void process_args(int argc, char **argv); + +/* tar2sqfs.c */ +extern sqfs_xattr_reader_t *xr; +extern sqfs_data_reader_t *data; +extern sqfs_super_t super; +extern ostream_t *out_file; + +char *assemble_tar_path(char *name, bool is_dir); + +/* xattr.c */ +int get_xattrs(const char *name, const sqfs_inode_generic_t *inode, + tar_xattr_t **out); + +/* write_tree.c */ +int write_tree(const sqfs_tree_node_t *n); + +#endif /* SQFS2TAR_H */ diff --git a/bin/sqfs2tar/src/write_tree.c b/bin/sqfs2tar/src/write_tree.c new file mode 100644 index 0000000..354ec21 --- /dev/null +++ b/bin/sqfs2tar/src/write_tree.c @@ -0,0 +1,209 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * write_tree.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "sqfs2tar.h" + +static sqfs_hard_link_t *links = NULL; +static unsigned int record_counter; + +static sqfs_hard_link_t *find_hard_link(const char *name, sqfs_u32 inum) +{ + sqfs_hard_link_t *lnk = NULL; + + for (lnk = links; lnk != NULL; lnk = lnk->next) { + if (lnk->inode_number == inum) { + if (strcmp(name, lnk->target) == 0) + lnk = NULL; + break; + } + } + + return lnk; +} + +static void inode_stat(const sqfs_tree_node_t *node, struct stat *sb) +{ + memset(sb, 0, sizeof(*sb)); + + sb->st_mode = node->inode->base.mode; + sb->st_uid = node->uid; + sb->st_gid = node->gid; + sb->st_mtime = node->inode->base.mod_time; + + switch (node->inode->base.type) { + case SQFS_INODE_BDEV: + case SQFS_INODE_CDEV: + sb->st_rdev = node->inode->data.dev.devno; + break; + case SQFS_INODE_EXT_BDEV: + case SQFS_INODE_EXT_CDEV: + sb->st_rdev = node->inode->data.dev_ext.devno; + break; + case SQFS_INODE_SLINK: + sb->st_size = node->inode->data.slink.target_size; + break; + case SQFS_INODE_EXT_SLINK: + sb->st_size = node->inode->data.slink_ext.target_size; + break; + case SQFS_INODE_FILE: + sb->st_size = node->inode->data.file.file_size; + break; + case SQFS_INODE_EXT_FILE: + sb->st_size = node->inode->data.file_ext.file_size; + break; + case SQFS_INODE_DIR: + sb->st_size = node->inode->data.dir.size; + break; + case SQFS_INODE_EXT_DIR: + sb->st_size = node->inode->data.dir_ext.size; + break; + default: + break; + } +} + +static int write_tree_dfs(const sqfs_tree_node_t *n) +{ + sqfs_hard_link_t *lnk = NULL; + tar_xattr_t *xattr = NULL; + char *name, *target; + struct stat sb; + size_t len; + int ret; + + inode_stat(n, &sb); + + if (n->parent == NULL) { + if (root_becomes == NULL) + goto skip_hdr; + + len = strlen(root_becomes); + name = malloc(len + 2); + if (name == NULL) { + perror("creating root directory"); + return -1; + } + + memcpy(name, root_becomes, len); + name[len] = '/'; + name[len + 1] = '\0'; + } else { + if (!is_filename_sane((const char *)n->name, false)) { + fprintf(stderr, "Found a file named '%s', skipping.\n", + n->name); + if (dont_skip) { + fputs("Not allowed to skip files, aborting!\n", + stderr); + return -1; + } + return 0; + } + + ret = sqfs_tree_node_get_path(n, &name); + if (ret != 0) { + sqfs_perror(NULL, "resolving tree node path", ret); + return -1; + } + + if (canonicalize_name(name)) + goto out_skip; + + name = assemble_tar_path(name, S_ISDIR(sb.st_mode)); + if (name == NULL) + return -1; + + lnk = find_hard_link(name, n->inode->base.inode_number); + if (lnk != NULL) { + ret = write_hard_link(out_file, &sb, name, lnk->target, + record_counter++); + sqfs_free(name); + return ret; + } + } + + if (!no_xattr) { + if (get_xattrs(name, n->inode, &xattr)) { + sqfs_free(name); + return -1; + } + } + + target = S_ISLNK(sb.st_mode) ? (char *)n->inode->extra : NULL; + ret = write_tar_header(out_file, &sb, name, target, xattr, + record_counter++); + free_xattr_list(xattr); + + if (ret > 0) + goto out_skip; + + if (ret < 0) { + sqfs_free(name); + return -1; + } + + if (S_ISREG(sb.st_mode)) { + if (sqfs_data_reader_dump(name, data, n->inode, out_file, + super.block_size)) { + sqfs_free(name); + return -1; + } + + if (padd_file(out_file, sb.st_size)) { + sqfs_free(name); + return -1; + } + } + + sqfs_free(name); +skip_hdr: + for (n = n->children; n != NULL; n = n->next) { + if (write_tree_dfs(n)) + return -1; + } + return 0; +out_skip: + if (dont_skip) { + fputs("Not allowed to skip files, aborting!\n", stderr); + ret = -1; + } else { + fprintf(stderr, "Skipping %s\n", name); + ret = 0; + } + sqfs_free(name); + return ret; +} + +int write_tree(const sqfs_tree_node_t *n) +{ + sqfs_hard_link_t *lnk; + int status = -1; + + if (!no_links) { + int ret = sqfs_tree_find_hard_links(n, &links); + if (ret) { + sqfs_perror(NULL, "detecting hard links in " + "file system tree", ret); + return -1; + } + + for (lnk = links; lnk != NULL; lnk = lnk->next) { + lnk->target = assemble_tar_path(lnk->target, false); + + if (lnk->target == NULL) + goto out_links; + } + } + + status = write_tree_dfs(n); +out_links: + while (links != NULL) { + lnk = links; + links = links->next; + sqfs_free(lnk->target); + free(lnk); + } + return status; +} diff --git a/bin/sqfs2tar/src/xattr.c b/bin/sqfs2tar/src/xattr.c new file mode 100644 index 0000000..abec4fb --- /dev/null +++ b/bin/sqfs2tar/src/xattr.c @@ -0,0 +1,91 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * xattr.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "sqfs2tar.h" + +static tar_xattr_t *mkxattr(const sqfs_xattr_entry_t *key, + const sqfs_xattr_value_t *value) +{ + tar_xattr_t *ent; + + ent = calloc(1, sizeof(*ent) + strlen((const char *)key->key) + + value->size + 2); + + if (ent == NULL) { + perror("creating xattr entry"); + return NULL; + } + + ent->key = ent->data; + ent->value = (sqfs_u8 *)ent->key + strlen((const char *)key->key) + 1; + ent->value_len = value->size; + + strcpy(ent->key, (const char *)key->key); + memcpy(ent->value, value->value, value->size + 1); + return ent; +} + +int get_xattrs(const char *name, const sqfs_inode_generic_t *inode, + tar_xattr_t **out) +{ + tar_xattr_t *list = NULL, *ent; + sqfs_xattr_value_t *value; + sqfs_xattr_entry_t *key; + sqfs_xattr_id_t desc; + sqfs_u32 index; + size_t i; + int ret; + + if (xr == NULL) + return 0; + + sqfs_inode_get_xattr_index(inode, &index); + if (index == 0xFFFFFFFF) + return 0; + + ret = sqfs_xattr_reader_get_desc(xr, index, &desc); + if (ret) { + sqfs_perror(name, "resolving xattr index", ret); + return -1; + } + + ret = sqfs_xattr_reader_seek_kv(xr, &desc); + if (ret) { + sqfs_perror(name, "locating xattr key-value pairs", ret); + return -1; + } + + for (i = 0; i < desc.count; ++i) { + ret = sqfs_xattr_reader_read_key(xr, &key); + if (ret) { + sqfs_perror(name, "reading xattr key", ret); + goto fail; + } + + ret = sqfs_xattr_reader_read_value(xr, key, &value); + if (ret) { + sqfs_perror(name, "reading xattr value", ret); + sqfs_free(key); + goto fail; + } + + ent = mkxattr(key, value); + sqfs_free(key); + sqfs_free(value); + + if (ent == NULL) + goto fail; + + ent->next = list; + list = ent; + } + + *out = list; + return 0; +fail: + free_xattr_list(list); + return -1; +} diff --git a/bin/sqfs2tar/write_tree.c b/bin/sqfs2tar/write_tree.c deleted file mode 100644 index 354ec21..0000000 --- a/bin/sqfs2tar/write_tree.c +++ /dev/null @@ -1,209 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * write_tree.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "sqfs2tar.h" - -static sqfs_hard_link_t *links = NULL; -static unsigned int record_counter; - -static sqfs_hard_link_t *find_hard_link(const char *name, sqfs_u32 inum) -{ - sqfs_hard_link_t *lnk = NULL; - - for (lnk = links; lnk != NULL; lnk = lnk->next) { - if (lnk->inode_number == inum) { - if (strcmp(name, lnk->target) == 0) - lnk = NULL; - break; - } - } - - return lnk; -} - -static void inode_stat(const sqfs_tree_node_t *node, struct stat *sb) -{ - memset(sb, 0, sizeof(*sb)); - - sb->st_mode = node->inode->base.mode; - sb->st_uid = node->uid; - sb->st_gid = node->gid; - sb->st_mtime = node->inode->base.mod_time; - - switch (node->inode->base.type) { - case SQFS_INODE_BDEV: - case SQFS_INODE_CDEV: - sb->st_rdev = node->inode->data.dev.devno; - break; - case SQFS_INODE_EXT_BDEV: - case SQFS_INODE_EXT_CDEV: - sb->st_rdev = node->inode->data.dev_ext.devno; - break; - case SQFS_INODE_SLINK: - sb->st_size = node->inode->data.slink.target_size; - break; - case SQFS_INODE_EXT_SLINK: - sb->st_size = node->inode->data.slink_ext.target_size; - break; - case SQFS_INODE_FILE: - sb->st_size = node->inode->data.file.file_size; - break; - case SQFS_INODE_EXT_FILE: - sb->st_size = node->inode->data.file_ext.file_size; - break; - case SQFS_INODE_DIR: - sb->st_size = node->inode->data.dir.size; - break; - case SQFS_INODE_EXT_DIR: - sb->st_size = node->inode->data.dir_ext.size; - break; - default: - break; - } -} - -static int write_tree_dfs(const sqfs_tree_node_t *n) -{ - sqfs_hard_link_t *lnk = NULL; - tar_xattr_t *xattr = NULL; - char *name, *target; - struct stat sb; - size_t len; - int ret; - - inode_stat(n, &sb); - - if (n->parent == NULL) { - if (root_becomes == NULL) - goto skip_hdr; - - len = strlen(root_becomes); - name = malloc(len + 2); - if (name == NULL) { - perror("creating root directory"); - return -1; - } - - memcpy(name, root_becomes, len); - name[len] = '/'; - name[len + 1] = '\0'; - } else { - if (!is_filename_sane((const char *)n->name, false)) { - fprintf(stderr, "Found a file named '%s', skipping.\n", - n->name); - if (dont_skip) { - fputs("Not allowed to skip files, aborting!\n", - stderr); - return -1; - } - return 0; - } - - ret = sqfs_tree_node_get_path(n, &name); - if (ret != 0) { - sqfs_perror(NULL, "resolving tree node path", ret); - return -1; - } - - if (canonicalize_name(name)) - goto out_skip; - - name = assemble_tar_path(name, S_ISDIR(sb.st_mode)); - if (name == NULL) - return -1; - - lnk = find_hard_link(name, n->inode->base.inode_number); - if (lnk != NULL) { - ret = write_hard_link(out_file, &sb, name, lnk->target, - record_counter++); - sqfs_free(name); - return ret; - } - } - - if (!no_xattr) { - if (get_xattrs(name, n->inode, &xattr)) { - sqfs_free(name); - return -1; - } - } - - target = S_ISLNK(sb.st_mode) ? (char *)n->inode->extra : NULL; - ret = write_tar_header(out_file, &sb, name, target, xattr, - record_counter++); - free_xattr_list(xattr); - - if (ret > 0) - goto out_skip; - - if (ret < 0) { - sqfs_free(name); - return -1; - } - - if (S_ISREG(sb.st_mode)) { - if (sqfs_data_reader_dump(name, data, n->inode, out_file, - super.block_size)) { - sqfs_free(name); - return -1; - } - - if (padd_file(out_file, sb.st_size)) { - sqfs_free(name); - return -1; - } - } - - sqfs_free(name); -skip_hdr: - for (n = n->children; n != NULL; n = n->next) { - if (write_tree_dfs(n)) - return -1; - } - return 0; -out_skip: - if (dont_skip) { - fputs("Not allowed to skip files, aborting!\n", stderr); - ret = -1; - } else { - fprintf(stderr, "Skipping %s\n", name); - ret = 0; - } - sqfs_free(name); - return ret; -} - -int write_tree(const sqfs_tree_node_t *n) -{ - sqfs_hard_link_t *lnk; - int status = -1; - - if (!no_links) { - int ret = sqfs_tree_find_hard_links(n, &links); - if (ret) { - sqfs_perror(NULL, "detecting hard links in " - "file system tree", ret); - return -1; - } - - for (lnk = links; lnk != NULL; lnk = lnk->next) { - lnk->target = assemble_tar_path(lnk->target, false); - - if (lnk->target == NULL) - goto out_links; - } - } - - status = write_tree_dfs(n); -out_links: - while (links != NULL) { - lnk = links; - links = links->next; - sqfs_free(lnk->target); - free(lnk); - } - return status; -} diff --git a/bin/sqfs2tar/xattr.c b/bin/sqfs2tar/xattr.c deleted file mode 100644 index abec4fb..0000000 --- a/bin/sqfs2tar/xattr.c +++ /dev/null @@ -1,91 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * xattr.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "sqfs2tar.h" - -static tar_xattr_t *mkxattr(const sqfs_xattr_entry_t *key, - const sqfs_xattr_value_t *value) -{ - tar_xattr_t *ent; - - ent = calloc(1, sizeof(*ent) + strlen((const char *)key->key) + - value->size + 2); - - if (ent == NULL) { - perror("creating xattr entry"); - return NULL; - } - - ent->key = ent->data; - ent->value = (sqfs_u8 *)ent->key + strlen((const char *)key->key) + 1; - ent->value_len = value->size; - - strcpy(ent->key, (const char *)key->key); - memcpy(ent->value, value->value, value->size + 1); - return ent; -} - -int get_xattrs(const char *name, const sqfs_inode_generic_t *inode, - tar_xattr_t **out) -{ - tar_xattr_t *list = NULL, *ent; - sqfs_xattr_value_t *value; - sqfs_xattr_entry_t *key; - sqfs_xattr_id_t desc; - sqfs_u32 index; - size_t i; - int ret; - - if (xr == NULL) - return 0; - - sqfs_inode_get_xattr_index(inode, &index); - if (index == 0xFFFFFFFF) - return 0; - - ret = sqfs_xattr_reader_get_desc(xr, index, &desc); - if (ret) { - sqfs_perror(name, "resolving xattr index", ret); - return -1; - } - - ret = sqfs_xattr_reader_seek_kv(xr, &desc); - if (ret) { - sqfs_perror(name, "locating xattr key-value pairs", ret); - return -1; - } - - for (i = 0; i < desc.count; ++i) { - ret = sqfs_xattr_reader_read_key(xr, &key); - if (ret) { - sqfs_perror(name, "reading xattr key", ret); - goto fail; - } - - ret = sqfs_xattr_reader_read_value(xr, key, &value); - if (ret) { - sqfs_perror(name, "reading xattr value", ret); - sqfs_free(key); - goto fail; - } - - ent = mkxattr(key, value); - sqfs_free(key); - sqfs_free(value); - - if (ent == NULL) - goto fail; - - ent->next = list; - list = ent; - } - - *out = list; - return 0; -fail: - free_xattr_list(list); - return -1; -} diff --git a/bin/sqfsdiff/Makemodule.am b/bin/sqfsdiff/Makemodule.am index ff08c7a..4f21901 100644 --- a/bin/sqfsdiff/Makemodule.am +++ b/bin/sqfsdiff/Makemodule.am @@ -1,8 +1,8 @@ -sqfsdiff_SOURCES = bin/sqfsdiff/sqfsdiff.c bin/sqfsdiff/sqfsdiff.h -sqfsdiff_SOURCES += bin/sqfsdiff/util.c bin/sqfsdiff/options.c -sqfsdiff_SOURCES += bin/sqfsdiff/compare_dir.c bin/sqfsdiff/node_compare.c -sqfsdiff_SOURCES += bin/sqfsdiff/compare_files.c bin/sqfsdiff/super.c -sqfsdiff_SOURCES += bin/sqfsdiff/extract.c +sqfsdiff_SOURCES = bin/sqfsdiff/src/sqfsdiff.c bin/sqfsdiff/src/sqfsdiff.h \ + bin/sqfsdiff/src/util.c bin/sqfsdiff/src/options.c \ + bin/sqfsdiff/src/compare_dir.c bin/sqfsdiff/src/node_compare.c \ + bin/sqfsdiff/src/compare_files.c bin/sqfsdiff/src/super.c \ + bin/sqfsdiff/src/extract.c sqfsdiff_CFLAGS = $(AM_CFLAGS) $(PTHREAD_CFLAGS) sqfsdiff_LDADD = libcommon.a libsquashfs.la libio.a libcompat.a libutil.a sqfsdiff_LDADD += $(LZO_LIBS) libfstree.a $(PTHREAD_LIBS) diff --git a/bin/sqfsdiff/compare_dir.c b/bin/sqfsdiff/compare_dir.c deleted file mode 100644 index 1a4c800..0000000 --- a/bin/sqfsdiff/compare_dir.c +++ /dev/null @@ -1,94 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * compare_dir.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "sqfsdiff.h" - -static int print_omitted(sqfsdiff_t *sd, bool is_old, sqfs_tree_node_t *n) -{ - char *path = node_path(n); - - if (path == NULL) - return -1; - - fprintf(stdout, "%c %s\n", is_old ? '<' : '>', path); - - if ((sd->compare_flags & COMPARE_EXTRACT_FILES) && - S_ISREG(n->inode->base.mode)) { - if (extract_files(sd, is_old ? n->inode : NULL, - is_old ? NULL : n->inode, path)) { - free(path); - return -1; - } - } - - free(path); - - for (n = n->children; n != NULL; n = n->next) { - if (print_omitted(sd, is_old, n)) - return -1; - } - - return 0; -} - -int compare_dir_entries(sqfsdiff_t *sd, sqfs_tree_node_t *old, - sqfs_tree_node_t *new) -{ - sqfs_tree_node_t *old_it = old->children, *old_prev = NULL; - sqfs_tree_node_t *new_it = new->children, *new_prev = NULL; - int ret, result = 0; - - while (old_it != NULL || new_it != NULL) { - if (old_it != NULL && new_it != NULL) { - ret = strcmp((const char *)old_it->name, - (const char *)new_it->name); - } else if (old_it == NULL) { - ret = 1; - } else { - ret = -1; - } - - if (ret < 0) { - result = 1; - - if (print_omitted(sd, true, old_it)) - return -1; - - if (old_prev == NULL) { - old->children = old_it->next; - sqfs_dir_tree_destroy(old_it); - old_it = old->children; - } else { - old_prev->next = old_it->next; - sqfs_dir_tree_destroy(old_it); - old_it = old_prev->next; - } - } else if (ret > 0) { - result = 1; - - if (print_omitted(sd, false, new_it)) - return -1; - - if (new_prev == NULL) { - new->children = new_it->next; - sqfs_dir_tree_destroy(new_it); - new_it = new->children; - } else { - new_prev->next = new_it->next; - sqfs_dir_tree_destroy(new_it); - new_it = new_prev->next; - } - } else { - old_prev = old_it; - old_it = old_it->next; - - new_prev = new_it; - new_it = new_it->next; - } - } - - return result; -} diff --git a/bin/sqfsdiff/compare_files.c b/bin/sqfsdiff/compare_files.c deleted file mode 100644 index 51b66bb..0000000 --- a/bin/sqfsdiff/compare_files.c +++ /dev/null @@ -1,72 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * compare_files.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "sqfsdiff.h" - -static unsigned char old_buf[MAX_WINDOW_SIZE]; -static unsigned char new_buf[MAX_WINDOW_SIZE]; - -static int read_blob(const char *prefix, const char *path, - sqfs_data_reader_t *rd, const sqfs_inode_generic_t *inode, - void *buffer, sqfs_u64 offset, size_t size) -{ - ssize_t ret; - - ret = sqfs_data_reader_read(rd, inode, offset, buffer, size); - ret = (ret < 0 || (size_t)ret < size) ? -1 : 0; - - if (ret) { - fprintf(stderr, "Failed to read %s from %s\n", - path, prefix); - return -1; - } - - return 0; -} - -int compare_files(sqfsdiff_t *sd, const sqfs_inode_generic_t *old, - const sqfs_inode_generic_t *new, const char *path) -{ - sqfs_u64 offset, diff, oldsz, newsz; - int status = 0, ret; - - sqfs_inode_get_file_size(old, &oldsz); - sqfs_inode_get_file_size(new, &newsz); - - if (oldsz != newsz) - goto out_different; - - if (sd->compare_flags & COMPARE_NO_CONTENTS) - return 0; - - for (offset = 0; offset < oldsz; offset += diff) { - diff = oldsz - offset; - - if (diff > MAX_WINDOW_SIZE) - diff = MAX_WINDOW_SIZE; - - ret = read_blob(sd->old_path, path, - sd->sqfs_old.data, old, old_buf, offset, diff); - if (ret) - return -1; - - ret = read_blob(sd->new_path, path, - sd->sqfs_new.data, new, new_buf, offset, diff); - if (ret) - return -1; - - if (memcmp(old_buf, new_buf, diff) != 0) - goto out_different; - } - - return status; -out_different: - if (sd->compare_flags & COMPARE_EXTRACT_FILES) { - if (extract_files(sd, old, new, path)) - return -1; - } - return 1; -} diff --git a/bin/sqfsdiff/extract.c b/bin/sqfsdiff/extract.c deleted file mode 100644 index f2072d4..0000000 --- a/bin/sqfsdiff/extract.c +++ /dev/null @@ -1,58 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * extract.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "sqfsdiff.h" - -static int extract(sqfs_data_reader_t *data, const sqfs_inode_generic_t *inode, - const char *prefix, const char *path, size_t block_size) -{ - char *ptr, *temp; - ostream_t *fp; - - temp = alloca(strlen(prefix) + strlen(path) + 2); - sprintf(temp, "%s/%s", prefix, path); - - ptr = strrchr(temp, '/'); - *ptr = '\0'; - if (mkdir_p(temp)) - return -1; - *ptr = '/'; - - fp = ostream_open_file(temp, OSTREAM_OPEN_OVERWRITE | - OSTREAM_OPEN_SPARSE); - if (fp == NULL) { - perror(temp); - return -1; - } - - if (sqfs_data_reader_dump(path, data, inode, fp, block_size)) { - sqfs_drop(fp); - return -1; - } - - ostream_flush(fp); - sqfs_drop(fp); - return 0; -} - -int extract_files(sqfsdiff_t *sd, const sqfs_inode_generic_t *old, - const sqfs_inode_generic_t *new, - const char *path) -{ - if (old != NULL) { - if (extract(sd->sqfs_old.data, old, "old", - path, sd->sqfs_old.super.block_size)) - return -1; - } - - if (new != NULL) { - if (extract(sd->sqfs_new.data, new, "new", - path, sd->sqfs_new.super.block_size)) - return -1; - } - - return 0; -} diff --git a/bin/sqfsdiff/node_compare.c b/bin/sqfsdiff/node_compare.c deleted file mode 100644 index a0c99c7..0000000 --- a/bin/sqfsdiff/node_compare.c +++ /dev/null @@ -1,206 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * node_compare.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "sqfsdiff.h" - -int node_compare(sqfsdiff_t *sd, sqfs_tree_node_t *a, sqfs_tree_node_t *b) -{ - sqfs_tree_node_t *ait, *bit; - bool promoted, demoted; - int ret, status = 0; - char *path; - - ret = sqfs_tree_node_get_path(a, &path); - if (ret != 0) { - sqfs_perror(NULL, "constructing absolute file path", ret); - return -1; - } - - if (a->inode->base.type != b->inode->base.type) { - promoted = demoted = false; - - switch (a->inode->base.type) { - case SQFS_INODE_DIR: - if (b->inode->base.type == SQFS_INODE_EXT_DIR) - promoted = true; - break; - case SQFS_INODE_FILE: - if (b->inode->base.type == SQFS_INODE_EXT_FILE) - promoted = true; - break; - case SQFS_INODE_SLINK: - if (b->inode->base.type == SQFS_INODE_EXT_SLINK) - promoted = true; - break; - case SQFS_INODE_BDEV: - if (b->inode->base.type == SQFS_INODE_EXT_BDEV) - promoted = true; - break; - case SQFS_INODE_CDEV: - if (b->inode->base.type == SQFS_INODE_EXT_CDEV) - promoted = true; - break; - case SQFS_INODE_FIFO: - if (b->inode->base.type == SQFS_INODE_EXT_FIFO) - promoted = true; - break; - case SQFS_INODE_SOCKET: - if (b->inode->base.type == SQFS_INODE_EXT_SOCKET) - promoted = true; - break; - case SQFS_INODE_EXT_DIR: - if (b->inode->base.type == SQFS_INODE_DIR) - demoted = true; - break; - case SQFS_INODE_EXT_FILE: - if (b->inode->base.type == SQFS_INODE_FILE) - demoted = true; - break; - case SQFS_INODE_EXT_SLINK: - if (b->inode->base.type == SQFS_INODE_SLINK) - demoted = true; - break; - case SQFS_INODE_EXT_BDEV: - if (b->inode->base.type == SQFS_INODE_BDEV) - demoted = true; - break; - case SQFS_INODE_EXT_CDEV: - if (b->inode->base.type == SQFS_INODE_CDEV) - demoted = true; - break; - case SQFS_INODE_EXT_FIFO: - if (b->inode->base.type == SQFS_INODE_FIFO) - demoted = true; - break; - case SQFS_INODE_EXT_SOCKET: - if (b->inode->base.type == SQFS_INODE_SOCKET) - demoted = true; - break; - default: - break; - } - - if (promoted) { - fprintf(stdout, "%s has an extended type\n", path); - status = 1; - } else if (demoted) { - fprintf(stdout, "%s has a basic type\n", path); - status = 1; - } else { - fprintf(stdout, "%s has a different type\n", path); - sqfs_free(path); - return 1; - } - } - - if (!(sd->compare_flags & COMPARE_NO_PERM)) { - if ((a->inode->base.mode & ~S_IFMT) != - (b->inode->base.mode & ~S_IFMT)) { - fprintf(stdout, "%s has different permissions\n", - path); - status = 1; - } - } - - if (!(sd->compare_flags & COMPARE_NO_OWNER)) { - if (a->uid != b->uid || a->gid != b->gid) { - fprintf(stdout, "%s has different ownership\n", path); - status = 1; - } - } - - if (sd->compare_flags & COMPARE_TIMESTAMP) { - if (a->inode->base.mod_time != b->inode->base.mod_time) { - fprintf(stdout, "%s has a different timestamp\n", path); - status = 1; - } - } - - if (sd->compare_flags & COMPARE_INODE_NUM) { - if (a->inode->base.inode_number != - b->inode->base.inode_number) { - fprintf(stdout, "%s has a different inode number\n", - path); - status = 1; - } - } - - switch (a->inode->base.type) { - case SQFS_INODE_SOCKET: - case SQFS_INODE_EXT_SOCKET: - case SQFS_INODE_FIFO: - case SQFS_INODE_EXT_FIFO: - break; - case SQFS_INODE_BDEV: - case SQFS_INODE_CDEV: - if (a->inode->data.dev.devno != b->inode->data.dev.devno) { - fprintf(stdout, "%s has different device number\n", - path); - status = 1; - } - break; - case SQFS_INODE_EXT_BDEV: - case SQFS_INODE_EXT_CDEV: - if (a->inode->data.dev_ext.devno != - b->inode->data.dev_ext.devno) { - fprintf(stdout, "%s has different device number\n", - path); - status = 1; - } - break; - case SQFS_INODE_SLINK: - case SQFS_INODE_EXT_SLINK: - if (strcmp((const char *)a->inode->extra, - (const char *)b->inode->extra)) { - fprintf(stdout, "%s has a different link target\n", - path); - } - break; - case SQFS_INODE_DIR: - case SQFS_INODE_EXT_DIR: - ret = compare_dir_entries(sd, a, b); - if (ret < 0) { - status = -1; - break; - } - if (ret > 0) - status = 1; - - sqfs_free(path); - path = NULL; - - ait = a->children; - bit = b->children; - - while (ait != NULL && bit != NULL) { - ret = node_compare(sd, ait, bit); - if (ret < 0) - return -1; - if (ret > 0) - status = 1; - - ait = ait->next; - bit = bit->next; - } - break; - case SQFS_INODE_FILE: - case SQFS_INODE_EXT_FILE: - ret = compare_files(sd, a->inode, b->inode, path); - if (ret < 0) { - status = -1; - } else if (ret > 0) { - fprintf(stdout, "regular file %s differs\n", path); - status = 1; - } - break; - default: - fprintf(stdout, "%s has unknown type, ignoring\n", path); - break; - } - - sqfs_free(path); - return status; -} diff --git a/bin/sqfsdiff/options.c b/bin/sqfsdiff/options.c deleted file mode 100644 index b8ce7f0..0000000 --- a/bin/sqfsdiff/options.c +++ /dev/null @@ -1,131 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * sqfsdiff.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "sqfsdiff.h" - -static struct option long_opts[] = { - { "old", required_argument, NULL, 'a' }, - { "new", required_argument, NULL, 'b' }, - { "no-owner", no_argument, NULL, 'O' }, - { "no-permissions", no_argument, NULL, 'P' }, - { "no-contents", no_argument, NULL, 'C' }, - { "timestamps", no_argument, NULL, 'T' }, - { "inode-num", no_argument, NULL, 'I' }, - { "super", no_argument, NULL, 'S' }, - { "extract", required_argument, NULL, 'e' }, - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, 'V' }, - { NULL, 0, NULL, 0 }, -}; - -static const char *short_opts = "a:b:OPCTISe:hV"; - -static const char *usagestr = -"Usage: sqfsdiff [OPTIONS...] --old,-a --new,-b \n" -"\n" -"Compare two squashfs images. In contrast to doing a direct diff of the\n" -"images, this actually parses the filesystems and generates a more\n" -"meaningful difference report.\n" -"\n" -"If only contents are compared, any differences in packed file layout,\n" -"ordering, compression, inode meta data and so on is ignored and the two\n" -"images are considered equal if each directory contains the same entries,\n" -"symlink with the same paths have the same targets, device nodes the same\n" -"device number and files the same size and contents.\n" -"\n" -"A report of any difference is printed to stdout. The exit status is similar\n" -"that of diff(1): 0 means equal, 1 means different, 2 means problem.\n" -"\n" -"Possible options:\n" -"\n" -" --old, -a The first of the two filesystems to compare.\n" -" --new, -b The second of the two filesystems to compare.\n" -"\n" -" --no-contents, -C Do not compare file contents.\n" -" --no-owner, -O Do not compare file owners.\n" -" --no-permissions, -P Do not compare permission bits.\n" -"\n" -" --timestamps, -T Compare file timestamps.\n" -" --inode-num, -I Compare inode numbers of all files.\n" -" --super, -S Also compare meta data in super blocks.\n" -"\n" -" --extract, -e Extract files that differ to the specified\n" -" directory. Contents of the first filesystem\n" -" end up in a subdirectory 'old' and of the\n" -" second filesystem in a subdirectory 'new'.\n" -"\n" -" --help, -h Print help text and exit.\n" -" --version, -V Print version information and exit.\n" -"\n"; - -void process_options(sqfsdiff_t *sd, int argc, char **argv) -{ - int i; - - for (;;) { - i = getopt_long(argc, argv, short_opts, long_opts, NULL); - if (i == -1) - break; - - switch (i) { - case 'a': - sd->old_path = optarg; - break; - case 'b': - sd->new_path = optarg; - break; - case 'O': - sd->compare_flags |= COMPARE_NO_OWNER; - break; - case 'P': - sd->compare_flags |= COMPARE_NO_PERM; - break; - case 'C': - sd->compare_flags |= COMPARE_NO_CONTENTS; - break; - case 'T': - sd->compare_flags |= COMPARE_TIMESTAMP; - break; - case 'I': - sd->compare_flags |= COMPARE_INODE_NUM; - break; - case 'S': - sd->compare_super = true; - break; - case 'e': - sd->compare_flags |= COMPARE_EXTRACT_FILES; - sd->extract_dir = optarg; - break; - case 'h': - fputs(usagestr, stdout); - exit(0); - case 'V': - print_version("sqfsdiff"); - exit(0); - default: - goto fail_arg; - } - } - - if (sd->old_path == NULL) { - fputs("Missing arguments: first filesystem\n", stderr); - goto fail_arg; - } - - if (sd->new_path == NULL) { - fputs("Missing arguments: second filesystem\n", stderr); - goto fail_arg; - } - - if (optind < argc) { - fputs("Unknown extra arguments\n", stderr); - goto fail_arg; - } - return; -fail_arg: - fprintf(stderr, "Try `sqfsdiff --help' for more information.\n"); - exit(2); -} diff --git a/bin/sqfsdiff/sqfsdiff.c b/bin/sqfsdiff/sqfsdiff.c deleted file mode 100644 index d789fe1..0000000 --- a/bin/sqfsdiff/sqfsdiff.c +++ /dev/null @@ -1,167 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * sqfsdiff.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "sqfsdiff.h" - -static void close_sfqs(sqfs_state_t *state) -{ - sqfs_drop(state->data); - sqfs_dir_tree_destroy(state->root); - sqfs_drop(state->dr); - sqfs_drop(state->idtbl); - sqfs_drop(state->cmp); - sqfs_drop(state->file); -} - -static int open_sfqs(sqfs_state_t *state, const char *path) -{ - int ret; - - memset(state, 0, sizeof(*state)); - - state->file = sqfs_open_file(path, SQFS_FILE_OPEN_READ_ONLY); - if (state->file == NULL) { - perror(path); - return -1; - } - - ret = sqfs_super_read(&state->super, state->file); - if (ret) { - sqfs_perror(path, "reading super block", ret); - goto fail; - } - - sqfs_compressor_config_init(&state->cfg, state->super.compression_id, - state->super.block_size, - SQFS_COMP_FLAG_UNCOMPRESS); - - ret = sqfs_compressor_create(&state->cfg, &state->cmp); - -#ifdef WITH_LZO - if (state->super.compression_id == SQFS_COMP_LZO && ret != 0) - ret = lzo_compressor_create(&state->cfg, &state->cmp); -#endif - - if (ret != 0) { - sqfs_perror(path, "creating compressor", ret); - goto fail; - } - - if (state->super.flags & SQFS_FLAG_COMPRESSOR_OPTIONS) { - ret = state->cmp->read_options(state->cmp, state->file); - - if (ret == 0) { - state->cmp->get_configuration(state->cmp, - &state->options); - state->have_options = true; - } else { - sqfs_perror(path, "reading compressor options", ret); - state->have_options = false; - } - } else { - state->have_options = false; - } - - state->idtbl = sqfs_id_table_create(0); - if (state->idtbl == NULL) { - sqfs_perror(path, "creating ID table", SQFS_ERROR_ALLOC); - goto fail; - } - - ret = sqfs_id_table_read(state->idtbl, state->file, - &state->super, state->cmp); - if (ret) { - sqfs_perror(path, "loading ID table", ret); - goto fail; - } - - state->dr = sqfs_dir_reader_create(&state->super, state->cmp, - state->file, 0); - if (state->dr == NULL) { - sqfs_perror(path, "creating directory reader", - SQFS_ERROR_ALLOC); - goto fail; - } - - ret = sqfs_dir_reader_get_full_hierarchy(state->dr, state->idtbl, - NULL, 0, &state->root); - if (ret) { - sqfs_perror(path, "loading filesystem tree", ret); - goto fail; - } - - state->data = sqfs_data_reader_create(state->file, - state->super.block_size, - state->cmp, 0); - if (state->data == NULL) { - sqfs_perror(path, "creating data reader", SQFS_ERROR_ALLOC); - goto fail; - } - - ret = sqfs_data_reader_load_fragment_table(state->data, &state->super); - if (ret) { - sqfs_perror(path, "loading fragment table", ret); - goto fail; - } - - return 0; -fail: - close_sfqs(state); - return -1; -} - -int main(int argc, char **argv) -{ - int status, ret = 0; - sqfsdiff_t sd; - - memset(&sd, 0, sizeof(sd)); - process_options(&sd, argc, argv); - - if (sd.extract_dir != NULL) { - if (mkdir_p(sd.extract_dir)) - return 2; - } - - if (open_sfqs(&sd.sqfs_old, sd.old_path)) - return 2; - - if (open_sfqs(&sd.sqfs_new, sd.new_path)) { - status = 2; - goto out_sqfs_old; - } - - if (sd.extract_dir != NULL) { - if (chdir(sd.extract_dir)) { - perror(sd.extract_dir); - ret = -1; - goto out; - } - } - - ret = node_compare(&sd, sd.sqfs_old.root, sd.sqfs_new.root); - if (ret != 0) - goto out; - - if (sd.compare_super) { - ret = compare_super_blocks(&sd.sqfs_old.super, - &sd.sqfs_new.super); - if (ret != 0) - goto out; - } -out: - if (ret < 0) { - status = 2; - } else if (ret > 0) { - status = 1; - } else { - status = 0; - } - close_sfqs(&sd.sqfs_new); -out_sqfs_old: - close_sfqs(&sd.sqfs_old); - return status; -} diff --git a/bin/sqfsdiff/sqfsdiff.h b/bin/sqfsdiff/sqfsdiff.h deleted file mode 100644 index 65e8120..0000000 --- a/bin/sqfsdiff/sqfsdiff.h +++ /dev/null @@ -1,73 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * sqfsdiff.h - * - * Copyright (C) 2019 David Oberhollenzer - */ -#ifndef DIFFTOOL_H -#define DIFFTOOL_H - -#include "config.h" -#include "common.h" -#include "fstree.h" -#include "util/util.h" - -#include -#include -#include -#include - -#define MAX_WINDOW_SIZE (1024 * 1024 * 4) - -typedef struct { - sqfs_compressor_config_t cfg; - sqfs_compressor_t *cmp; - sqfs_super_t super; - sqfs_file_t *file; - sqfs_id_table_t *idtbl; - sqfs_dir_reader_t *dr; - sqfs_tree_node_t *root; - sqfs_data_reader_t *data; - - sqfs_compressor_config_t options; - bool have_options; -} sqfs_state_t; - -typedef struct { - const char *old_path; - const char *new_path; - int compare_flags; - sqfs_state_t sqfs_old; - sqfs_state_t sqfs_new; - bool compare_super; - const char *extract_dir; -} sqfsdiff_t; - -enum { - COMPARE_NO_PERM = 0x01, - COMPARE_NO_OWNER = 0x02, - COMPARE_NO_CONTENTS = 0x04, - COMPARE_TIMESTAMP = 0x08, - COMPARE_INODE_NUM = 0x10, - COMPARE_EXTRACT_FILES = 0x20, -}; - -int compare_dir_entries(sqfsdiff_t *sd, sqfs_tree_node_t *old, - sqfs_tree_node_t *new); - -char *node_path(const sqfs_tree_node_t *n); - -int compare_files(sqfsdiff_t *sd, const sqfs_inode_generic_t *old, - const sqfs_inode_generic_t *new, const char *path); - -int node_compare(sqfsdiff_t *sd, sqfs_tree_node_t *a, sqfs_tree_node_t *b); - -int compare_super_blocks(const sqfs_super_t *a, const sqfs_super_t *b); - -int extract_files(sqfsdiff_t *sd, const sqfs_inode_generic_t *old, - const sqfs_inode_generic_t *new, - const char *path); - -void process_options(sqfsdiff_t *sd, int argc, char **argv); - -#endif /* DIFFTOOL_H */ diff --git a/bin/sqfsdiff/src/compare_dir.c b/bin/sqfsdiff/src/compare_dir.c new file mode 100644 index 0000000..1a4c800 --- /dev/null +++ b/bin/sqfsdiff/src/compare_dir.c @@ -0,0 +1,94 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * compare_dir.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "sqfsdiff.h" + +static int print_omitted(sqfsdiff_t *sd, bool is_old, sqfs_tree_node_t *n) +{ + char *path = node_path(n); + + if (path == NULL) + return -1; + + fprintf(stdout, "%c %s\n", is_old ? '<' : '>', path); + + if ((sd->compare_flags & COMPARE_EXTRACT_FILES) && + S_ISREG(n->inode->base.mode)) { + if (extract_files(sd, is_old ? n->inode : NULL, + is_old ? NULL : n->inode, path)) { + free(path); + return -1; + } + } + + free(path); + + for (n = n->children; n != NULL; n = n->next) { + if (print_omitted(sd, is_old, n)) + return -1; + } + + return 0; +} + +int compare_dir_entries(sqfsdiff_t *sd, sqfs_tree_node_t *old, + sqfs_tree_node_t *new) +{ + sqfs_tree_node_t *old_it = old->children, *old_prev = NULL; + sqfs_tree_node_t *new_it = new->children, *new_prev = NULL; + int ret, result = 0; + + while (old_it != NULL || new_it != NULL) { + if (old_it != NULL && new_it != NULL) { + ret = strcmp((const char *)old_it->name, + (const char *)new_it->name); + } else if (old_it == NULL) { + ret = 1; + } else { + ret = -1; + } + + if (ret < 0) { + result = 1; + + if (print_omitted(sd, true, old_it)) + return -1; + + if (old_prev == NULL) { + old->children = old_it->next; + sqfs_dir_tree_destroy(old_it); + old_it = old->children; + } else { + old_prev->next = old_it->next; + sqfs_dir_tree_destroy(old_it); + old_it = old_prev->next; + } + } else if (ret > 0) { + result = 1; + + if (print_omitted(sd, false, new_it)) + return -1; + + if (new_prev == NULL) { + new->children = new_it->next; + sqfs_dir_tree_destroy(new_it); + new_it = new->children; + } else { + new_prev->next = new_it->next; + sqfs_dir_tree_destroy(new_it); + new_it = new_prev->next; + } + } else { + old_prev = old_it; + old_it = old_it->next; + + new_prev = new_it; + new_it = new_it->next; + } + } + + return result; +} diff --git a/bin/sqfsdiff/src/compare_files.c b/bin/sqfsdiff/src/compare_files.c new file mode 100644 index 0000000..51b66bb --- /dev/null +++ b/bin/sqfsdiff/src/compare_files.c @@ -0,0 +1,72 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * compare_files.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "sqfsdiff.h" + +static unsigned char old_buf[MAX_WINDOW_SIZE]; +static unsigned char new_buf[MAX_WINDOW_SIZE]; + +static int read_blob(const char *prefix, const char *path, + sqfs_data_reader_t *rd, const sqfs_inode_generic_t *inode, + void *buffer, sqfs_u64 offset, size_t size) +{ + ssize_t ret; + + ret = sqfs_data_reader_read(rd, inode, offset, buffer, size); + ret = (ret < 0 || (size_t)ret < size) ? -1 : 0; + + if (ret) { + fprintf(stderr, "Failed to read %s from %s\n", + path, prefix); + return -1; + } + + return 0; +} + +int compare_files(sqfsdiff_t *sd, const sqfs_inode_generic_t *old, + const sqfs_inode_generic_t *new, const char *path) +{ + sqfs_u64 offset, diff, oldsz, newsz; + int status = 0, ret; + + sqfs_inode_get_file_size(old, &oldsz); + sqfs_inode_get_file_size(new, &newsz); + + if (oldsz != newsz) + goto out_different; + + if (sd->compare_flags & COMPARE_NO_CONTENTS) + return 0; + + for (offset = 0; offset < oldsz; offset += diff) { + diff = oldsz - offset; + + if (diff > MAX_WINDOW_SIZE) + diff = MAX_WINDOW_SIZE; + + ret = read_blob(sd->old_path, path, + sd->sqfs_old.data, old, old_buf, offset, diff); + if (ret) + return -1; + + ret = read_blob(sd->new_path, path, + sd->sqfs_new.data, new, new_buf, offset, diff); + if (ret) + return -1; + + if (memcmp(old_buf, new_buf, diff) != 0) + goto out_different; + } + + return status; +out_different: + if (sd->compare_flags & COMPARE_EXTRACT_FILES) { + if (extract_files(sd, old, new, path)) + return -1; + } + return 1; +} diff --git a/bin/sqfsdiff/src/extract.c b/bin/sqfsdiff/src/extract.c new file mode 100644 index 0000000..f2072d4 --- /dev/null +++ b/bin/sqfsdiff/src/extract.c @@ -0,0 +1,58 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * extract.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "sqfsdiff.h" + +static int extract(sqfs_data_reader_t *data, const sqfs_inode_generic_t *inode, + const char *prefix, const char *path, size_t block_size) +{ + char *ptr, *temp; + ostream_t *fp; + + temp = alloca(strlen(prefix) + strlen(path) + 2); + sprintf(temp, "%s/%s", prefix, path); + + ptr = strrchr(temp, '/'); + *ptr = '\0'; + if (mkdir_p(temp)) + return -1; + *ptr = '/'; + + fp = ostream_open_file(temp, OSTREAM_OPEN_OVERWRITE | + OSTREAM_OPEN_SPARSE); + if (fp == NULL) { + perror(temp); + return -1; + } + + if (sqfs_data_reader_dump(path, data, inode, fp, block_size)) { + sqfs_drop(fp); + return -1; + } + + ostream_flush(fp); + sqfs_drop(fp); + return 0; +} + +int extract_files(sqfsdiff_t *sd, const sqfs_inode_generic_t *old, + const sqfs_inode_generic_t *new, + const char *path) +{ + if (old != NULL) { + if (extract(sd->sqfs_old.data, old, "old", + path, sd->sqfs_old.super.block_size)) + return -1; + } + + if (new != NULL) { + if (extract(sd->sqfs_new.data, new, "new", + path, sd->sqfs_new.super.block_size)) + return -1; + } + + return 0; +} diff --git a/bin/sqfsdiff/src/node_compare.c b/bin/sqfsdiff/src/node_compare.c new file mode 100644 index 0000000..a0c99c7 --- /dev/null +++ b/bin/sqfsdiff/src/node_compare.c @@ -0,0 +1,206 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * node_compare.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "sqfsdiff.h" + +int node_compare(sqfsdiff_t *sd, sqfs_tree_node_t *a, sqfs_tree_node_t *b) +{ + sqfs_tree_node_t *ait, *bit; + bool promoted, demoted; + int ret, status = 0; + char *path; + + ret = sqfs_tree_node_get_path(a, &path); + if (ret != 0) { + sqfs_perror(NULL, "constructing absolute file path", ret); + return -1; + } + + if (a->inode->base.type != b->inode->base.type) { + promoted = demoted = false; + + switch (a->inode->base.type) { + case SQFS_INODE_DIR: + if (b->inode->base.type == SQFS_INODE_EXT_DIR) + promoted = true; + break; + case SQFS_INODE_FILE: + if (b->inode->base.type == SQFS_INODE_EXT_FILE) + promoted = true; + break; + case SQFS_INODE_SLINK: + if (b->inode->base.type == SQFS_INODE_EXT_SLINK) + promoted = true; + break; + case SQFS_INODE_BDEV: + if (b->inode->base.type == SQFS_INODE_EXT_BDEV) + promoted = true; + break; + case SQFS_INODE_CDEV: + if (b->inode->base.type == SQFS_INODE_EXT_CDEV) + promoted = true; + break; + case SQFS_INODE_FIFO: + if (b->inode->base.type == SQFS_INODE_EXT_FIFO) + promoted = true; + break; + case SQFS_INODE_SOCKET: + if (b->inode->base.type == SQFS_INODE_EXT_SOCKET) + promoted = true; + break; + case SQFS_INODE_EXT_DIR: + if (b->inode->base.type == SQFS_INODE_DIR) + demoted = true; + break; + case SQFS_INODE_EXT_FILE: + if (b->inode->base.type == SQFS_INODE_FILE) + demoted = true; + break; + case SQFS_INODE_EXT_SLINK: + if (b->inode->base.type == SQFS_INODE_SLINK) + demoted = true; + break; + case SQFS_INODE_EXT_BDEV: + if (b->inode->base.type == SQFS_INODE_BDEV) + demoted = true; + break; + case SQFS_INODE_EXT_CDEV: + if (b->inode->base.type == SQFS_INODE_CDEV) + demoted = true; + break; + case SQFS_INODE_EXT_FIFO: + if (b->inode->base.type == SQFS_INODE_FIFO) + demoted = true; + break; + case SQFS_INODE_EXT_SOCKET: + if (b->inode->base.type == SQFS_INODE_SOCKET) + demoted = true; + break; + default: + break; + } + + if (promoted) { + fprintf(stdout, "%s has an extended type\n", path); + status = 1; + } else if (demoted) { + fprintf(stdout, "%s has a basic type\n", path); + status = 1; + } else { + fprintf(stdout, "%s has a different type\n", path); + sqfs_free(path); + return 1; + } + } + + if (!(sd->compare_flags & COMPARE_NO_PERM)) { + if ((a->inode->base.mode & ~S_IFMT) != + (b->inode->base.mode & ~S_IFMT)) { + fprintf(stdout, "%s has different permissions\n", + path); + status = 1; + } + } + + if (!(sd->compare_flags & COMPARE_NO_OWNER)) { + if (a->uid != b->uid || a->gid != b->gid) { + fprintf(stdout, "%s has different ownership\n", path); + status = 1; + } + } + + if (sd->compare_flags & COMPARE_TIMESTAMP) { + if (a->inode->base.mod_time != b->inode->base.mod_time) { + fprintf(stdout, "%s has a different timestamp\n", path); + status = 1; + } + } + + if (sd->compare_flags & COMPARE_INODE_NUM) { + if (a->inode->base.inode_number != + b->inode->base.inode_number) { + fprintf(stdout, "%s has a different inode number\n", + path); + status = 1; + } + } + + switch (a->inode->base.type) { + case SQFS_INODE_SOCKET: + case SQFS_INODE_EXT_SOCKET: + case SQFS_INODE_FIFO: + case SQFS_INODE_EXT_FIFO: + break; + case SQFS_INODE_BDEV: + case SQFS_INODE_CDEV: + if (a->inode->data.dev.devno != b->inode->data.dev.devno) { + fprintf(stdout, "%s has different device number\n", + path); + status = 1; + } + break; + case SQFS_INODE_EXT_BDEV: + case SQFS_INODE_EXT_CDEV: + if (a->inode->data.dev_ext.devno != + b->inode->data.dev_ext.devno) { + fprintf(stdout, "%s has different device number\n", + path); + status = 1; + } + break; + case SQFS_INODE_SLINK: + case SQFS_INODE_EXT_SLINK: + if (strcmp((const char *)a->inode->extra, + (const char *)b->inode->extra)) { + fprintf(stdout, "%s has a different link target\n", + path); + } + break; + case SQFS_INODE_DIR: + case SQFS_INODE_EXT_DIR: + ret = compare_dir_entries(sd, a, b); + if (ret < 0) { + status = -1; + break; + } + if (ret > 0) + status = 1; + + sqfs_free(path); + path = NULL; + + ait = a->children; + bit = b->children; + + while (ait != NULL && bit != NULL) { + ret = node_compare(sd, ait, bit); + if (ret < 0) + return -1; + if (ret > 0) + status = 1; + + ait = ait->next; + bit = bit->next; + } + break; + case SQFS_INODE_FILE: + case SQFS_INODE_EXT_FILE: + ret = compare_files(sd, a->inode, b->inode, path); + if (ret < 0) { + status = -1; + } else if (ret > 0) { + fprintf(stdout, "regular file %s differs\n", path); + status = 1; + } + break; + default: + fprintf(stdout, "%s has unknown type, ignoring\n", path); + break; + } + + sqfs_free(path); + return status; +} diff --git a/bin/sqfsdiff/src/options.c b/bin/sqfsdiff/src/options.c new file mode 100644 index 0000000..b8ce7f0 --- /dev/null +++ b/bin/sqfsdiff/src/options.c @@ -0,0 +1,131 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * sqfsdiff.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "sqfsdiff.h" + +static struct option long_opts[] = { + { "old", required_argument, NULL, 'a' }, + { "new", required_argument, NULL, 'b' }, + { "no-owner", no_argument, NULL, 'O' }, + { "no-permissions", no_argument, NULL, 'P' }, + { "no-contents", no_argument, NULL, 'C' }, + { "timestamps", no_argument, NULL, 'T' }, + { "inode-num", no_argument, NULL, 'I' }, + { "super", no_argument, NULL, 'S' }, + { "extract", required_argument, NULL, 'e' }, + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, 'V' }, + { NULL, 0, NULL, 0 }, +}; + +static const char *short_opts = "a:b:OPCTISe:hV"; + +static const char *usagestr = +"Usage: sqfsdiff [OPTIONS...] --old,-a --new,-b \n" +"\n" +"Compare two squashfs images. In contrast to doing a direct diff of the\n" +"images, this actually parses the filesystems and generates a more\n" +"meaningful difference report.\n" +"\n" +"If only contents are compared, any differences in packed file layout,\n" +"ordering, compression, inode meta data and so on is ignored and the two\n" +"images are considered equal if each directory contains the same entries,\n" +"symlink with the same paths have the same targets, device nodes the same\n" +"device number and files the same size and contents.\n" +"\n" +"A report of any difference is printed to stdout. The exit status is similar\n" +"that of diff(1): 0 means equal, 1 means different, 2 means problem.\n" +"\n" +"Possible options:\n" +"\n" +" --old, -a The first of the two filesystems to compare.\n" +" --new, -b The second of the two filesystems to compare.\n" +"\n" +" --no-contents, -C Do not compare file contents.\n" +" --no-owner, -O Do not compare file owners.\n" +" --no-permissions, -P Do not compare permission bits.\n" +"\n" +" --timestamps, -T Compare file timestamps.\n" +" --inode-num, -I Compare inode numbers of all files.\n" +" --super, -S Also compare meta data in super blocks.\n" +"\n" +" --extract, -e Extract files that differ to the specified\n" +" directory. Contents of the first filesystem\n" +" end up in a subdirectory 'old' and of the\n" +" second filesystem in a subdirectory 'new'.\n" +"\n" +" --help, -h Print help text and exit.\n" +" --version, -V Print version information and exit.\n" +"\n"; + +void process_options(sqfsdiff_t *sd, int argc, char **argv) +{ + int i; + + for (;;) { + i = getopt_long(argc, argv, short_opts, long_opts, NULL); + if (i == -1) + break; + + switch (i) { + case 'a': + sd->old_path = optarg; + break; + case 'b': + sd->new_path = optarg; + break; + case 'O': + sd->compare_flags |= COMPARE_NO_OWNER; + break; + case 'P': + sd->compare_flags |= COMPARE_NO_PERM; + break; + case 'C': + sd->compare_flags |= COMPARE_NO_CONTENTS; + break; + case 'T': + sd->compare_flags |= COMPARE_TIMESTAMP; + break; + case 'I': + sd->compare_flags |= COMPARE_INODE_NUM; + break; + case 'S': + sd->compare_super = true; + break; + case 'e': + sd->compare_flags |= COMPARE_EXTRACT_FILES; + sd->extract_dir = optarg; + break; + case 'h': + fputs(usagestr, stdout); + exit(0); + case 'V': + print_version("sqfsdiff"); + exit(0); + default: + goto fail_arg; + } + } + + if (sd->old_path == NULL) { + fputs("Missing arguments: first filesystem\n", stderr); + goto fail_arg; + } + + if (sd->new_path == NULL) { + fputs("Missing arguments: second filesystem\n", stderr); + goto fail_arg; + } + + if (optind < argc) { + fputs("Unknown extra arguments\n", stderr); + goto fail_arg; + } + return; +fail_arg: + fprintf(stderr, "Try `sqfsdiff --help' for more information.\n"); + exit(2); +} diff --git a/bin/sqfsdiff/src/sqfsdiff.c b/bin/sqfsdiff/src/sqfsdiff.c new file mode 100644 index 0000000..d789fe1 --- /dev/null +++ b/bin/sqfsdiff/src/sqfsdiff.c @@ -0,0 +1,167 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * sqfsdiff.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "sqfsdiff.h" + +static void close_sfqs(sqfs_state_t *state) +{ + sqfs_drop(state->data); + sqfs_dir_tree_destroy(state->root); + sqfs_drop(state->dr); + sqfs_drop(state->idtbl); + sqfs_drop(state->cmp); + sqfs_drop(state->file); +} + +static int open_sfqs(sqfs_state_t *state, const char *path) +{ + int ret; + + memset(state, 0, sizeof(*state)); + + state->file = sqfs_open_file(path, SQFS_FILE_OPEN_READ_ONLY); + if (state->file == NULL) { + perror(path); + return -1; + } + + ret = sqfs_super_read(&state->super, state->file); + if (ret) { + sqfs_perror(path, "reading super block", ret); + goto fail; + } + + sqfs_compressor_config_init(&state->cfg, state->super.compression_id, + state->super.block_size, + SQFS_COMP_FLAG_UNCOMPRESS); + + ret = sqfs_compressor_create(&state->cfg, &state->cmp); + +#ifdef WITH_LZO + if (state->super.compression_id == SQFS_COMP_LZO && ret != 0) + ret = lzo_compressor_create(&state->cfg, &state->cmp); +#endif + + if (ret != 0) { + sqfs_perror(path, "creating compressor", ret); + goto fail; + } + + if (state->super.flags & SQFS_FLAG_COMPRESSOR_OPTIONS) { + ret = state->cmp->read_options(state->cmp, state->file); + + if (ret == 0) { + state->cmp->get_configuration(state->cmp, + &state->options); + state->have_options = true; + } else { + sqfs_perror(path, "reading compressor options", ret); + state->have_options = false; + } + } else { + state->have_options = false; + } + + state->idtbl = sqfs_id_table_create(0); + if (state->idtbl == NULL) { + sqfs_perror(path, "creating ID table", SQFS_ERROR_ALLOC); + goto fail; + } + + ret = sqfs_id_table_read(state->idtbl, state->file, + &state->super, state->cmp); + if (ret) { + sqfs_perror(path, "loading ID table", ret); + goto fail; + } + + state->dr = sqfs_dir_reader_create(&state->super, state->cmp, + state->file, 0); + if (state->dr == NULL) { + sqfs_perror(path, "creating directory reader", + SQFS_ERROR_ALLOC); + goto fail; + } + + ret = sqfs_dir_reader_get_full_hierarchy(state->dr, state->idtbl, + NULL, 0, &state->root); + if (ret) { + sqfs_perror(path, "loading filesystem tree", ret); + goto fail; + } + + state->data = sqfs_data_reader_create(state->file, + state->super.block_size, + state->cmp, 0); + if (state->data == NULL) { + sqfs_perror(path, "creating data reader", SQFS_ERROR_ALLOC); + goto fail; + } + + ret = sqfs_data_reader_load_fragment_table(state->data, &state->super); + if (ret) { + sqfs_perror(path, "loading fragment table", ret); + goto fail; + } + + return 0; +fail: + close_sfqs(state); + return -1; +} + +int main(int argc, char **argv) +{ + int status, ret = 0; + sqfsdiff_t sd; + + memset(&sd, 0, sizeof(sd)); + process_options(&sd, argc, argv); + + if (sd.extract_dir != NULL) { + if (mkdir_p(sd.extract_dir)) + return 2; + } + + if (open_sfqs(&sd.sqfs_old, sd.old_path)) + return 2; + + if (open_sfqs(&sd.sqfs_new, sd.new_path)) { + status = 2; + goto out_sqfs_old; + } + + if (sd.extract_dir != NULL) { + if (chdir(sd.extract_dir)) { + perror(sd.extract_dir); + ret = -1; + goto out; + } + } + + ret = node_compare(&sd, sd.sqfs_old.root, sd.sqfs_new.root); + if (ret != 0) + goto out; + + if (sd.compare_super) { + ret = compare_super_blocks(&sd.sqfs_old.super, + &sd.sqfs_new.super); + if (ret != 0) + goto out; + } +out: + if (ret < 0) { + status = 2; + } else if (ret > 0) { + status = 1; + } else { + status = 0; + } + close_sfqs(&sd.sqfs_new); +out_sqfs_old: + close_sfqs(&sd.sqfs_old); + return status; +} diff --git a/bin/sqfsdiff/src/sqfsdiff.h b/bin/sqfsdiff/src/sqfsdiff.h new file mode 100644 index 0000000..65e8120 --- /dev/null +++ b/bin/sqfsdiff/src/sqfsdiff.h @@ -0,0 +1,73 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * sqfsdiff.h + * + * Copyright (C) 2019 David Oberhollenzer + */ +#ifndef DIFFTOOL_H +#define DIFFTOOL_H + +#include "config.h" +#include "common.h" +#include "fstree.h" +#include "util/util.h" + +#include +#include +#include +#include + +#define MAX_WINDOW_SIZE (1024 * 1024 * 4) + +typedef struct { + sqfs_compressor_config_t cfg; + sqfs_compressor_t *cmp; + sqfs_super_t super; + sqfs_file_t *file; + sqfs_id_table_t *idtbl; + sqfs_dir_reader_t *dr; + sqfs_tree_node_t *root; + sqfs_data_reader_t *data; + + sqfs_compressor_config_t options; + bool have_options; +} sqfs_state_t; + +typedef struct { + const char *old_path; + const char *new_path; + int compare_flags; + sqfs_state_t sqfs_old; + sqfs_state_t sqfs_new; + bool compare_super; + const char *extract_dir; +} sqfsdiff_t; + +enum { + COMPARE_NO_PERM = 0x01, + COMPARE_NO_OWNER = 0x02, + COMPARE_NO_CONTENTS = 0x04, + COMPARE_TIMESTAMP = 0x08, + COMPARE_INODE_NUM = 0x10, + COMPARE_EXTRACT_FILES = 0x20, +}; + +int compare_dir_entries(sqfsdiff_t *sd, sqfs_tree_node_t *old, + sqfs_tree_node_t *new); + +char *node_path(const sqfs_tree_node_t *n); + +int compare_files(sqfsdiff_t *sd, const sqfs_inode_generic_t *old, + const sqfs_inode_generic_t *new, const char *path); + +int node_compare(sqfsdiff_t *sd, sqfs_tree_node_t *a, sqfs_tree_node_t *b); + +int compare_super_blocks(const sqfs_super_t *a, const sqfs_super_t *b); + +int extract_files(sqfsdiff_t *sd, const sqfs_inode_generic_t *old, + const sqfs_inode_generic_t *new, + const char *path); + +void process_options(sqfsdiff_t *sd, int argc, char **argv); + +#endif /* DIFFTOOL_H */ diff --git a/bin/sqfsdiff/src/super.c b/bin/sqfsdiff/src/super.c new file mode 100644 index 0000000..0cf18e0 --- /dev/null +++ b/bin/sqfsdiff/src/super.c @@ -0,0 +1,125 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * super.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "sqfsdiff.h" + +static const struct { + sqfs_u16 mask; + const char *name; +} sqfs_flags[] = { + { SQFS_FLAG_UNCOMPRESSED_INODES, "uncompressed inodes" }, + { SQFS_FLAG_UNCOMPRESSED_DATA, "uncompressed data" }, + { SQFS_FLAG_UNCOMPRESSED_FRAGMENTS, "uncompressed fragments" }, + { SQFS_FLAG_NO_FRAGMENTS, "no fragments" }, + { SQFS_FLAG_ALWAYS_FRAGMENTS, "always fragments" }, + { SQFS_FLAG_NO_DUPLICATES, "no duplicates" }, + { SQFS_FLAG_EXPORTABLE, "exportable" }, + { SQFS_FLAG_UNCOMPRESSED_XATTRS, "uncompressed xattrs" }, + { SQFS_FLAG_NO_XATTRS, "no xattrs" }, + { SQFS_FLAG_COMPRESSOR_OPTIONS, "compressor options" }, + { SQFS_FLAG_UNCOMPRESSED_IDS, "uncompressed ids" }, +}; + +static void print_value_difference(const char *name, sqfs_u64 a, sqfs_u64 b) +{ + sqfs_u64 diff; + char c; + + if (a != b) { + if (a < b) { + c = '+'; + diff = b - a; + } else { + c = '-'; + diff = a - b; + } + fprintf(stdout, "%s: %c%lu\n", name, c, + (unsigned long)diff); + } +} + +static void print_offset_diff(const char *name, sqfs_u64 a, sqfs_u64 b) +{ + if (a != b) + fprintf(stdout, "Location of %s differs\n", name); +} + +static void print_flag_diff(sqfs_u16 a, sqfs_u16 b) +{ + sqfs_u16 diff = a ^ b, mask; + size_t i; + char c; + + if (diff == 0) + return; + + fputs("flags:\n", stdout); + + for (i = 0; i < sizeof(sqfs_flags) / sizeof(sqfs_flags[0]); ++i) { + if (diff & sqfs_flags[i].mask) { + c = a & sqfs_flags[i].mask ? '<' : '>'; + + fprintf(stdout, "\t%c%s\n", c, sqfs_flags[i].name); + } + + a &= ~sqfs_flags[i].mask; + b &= ~sqfs_flags[i].mask; + diff &= ~sqfs_flags[i].mask; + } + + for (i = 0, mask = 0x01; i < 16; ++i, mask <<= 1) { + if (diff & mask) { + fprintf(stdout, "\t%c additional unknown\n", + a & mask ? '<' : '>'); + } + } +} + +int compare_super_blocks(const sqfs_super_t *a, const sqfs_super_t *b) +{ + if (memcmp(a, b, sizeof(*a)) == 0) + return 0; + + fputs("======== super blocks are different ========\n", stdout); + + /* TODO: if a new magic number or squashfs version is introduced, + compare them. */ + + print_value_difference("inode count", a->inode_count, b->inode_count); + print_value_difference("modification time", a->modification_time, + b->modification_time); + print_value_difference("block size", a->block_size, b->block_size); + print_value_difference("block log", a->block_log, b->block_log); + print_value_difference("fragment table entries", + a->fragment_entry_count, + b->fragment_entry_count); + print_value_difference("ID table entries", a->id_count, b->id_count); + + if (a->compression_id != b->compression_id) { + fprintf(stdout, "compressor: %s vs %s\n", + sqfs_compressor_name_from_id(a->compression_id), + sqfs_compressor_name_from_id(b->compression_id)); + } + + print_flag_diff(a->flags, b->flags); + + print_value_difference("total bytes used", a->bytes_used, + b->bytes_used); + + print_offset_diff("root inode", a->root_inode_ref, b->root_inode_ref); + print_offset_diff("ID table", a->id_table_start, b->id_table_start); + print_offset_diff("xattr ID table", a->xattr_id_table_start, + b->xattr_id_table_start); + print_offset_diff("inode table", a->inode_table_start, + b->inode_table_start); + print_offset_diff("directory table", a->directory_table_start, + b->directory_table_start); + print_offset_diff("fragment table", a->fragment_table_start, + b->fragment_table_start); + print_offset_diff("export table", a->export_table_start, + b->export_table_start); + return 1; +} diff --git a/bin/sqfsdiff/src/util.c b/bin/sqfsdiff/src/util.c new file mode 100644 index 0000000..a11770f --- /dev/null +++ b/bin/sqfsdiff/src/util.c @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * util.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "sqfsdiff.h" + +char *node_path(const sqfs_tree_node_t *n) +{ + char *path; + int ret; + + ret = sqfs_tree_node_get_path(n, &path); + if (ret != 0) { + sqfs_perror(NULL, "get path", ret); + return NULL; + } + + if (canonicalize_name(path)) { + fprintf(stderr, "failed to canonicalization '%s'\n", path); + sqfs_free(path); + return NULL; + } + + return path; +} diff --git a/bin/sqfsdiff/super.c b/bin/sqfsdiff/super.c deleted file mode 100644 index 0cf18e0..0000000 --- a/bin/sqfsdiff/super.c +++ /dev/null @@ -1,125 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * super.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "sqfsdiff.h" - -static const struct { - sqfs_u16 mask; - const char *name; -} sqfs_flags[] = { - { SQFS_FLAG_UNCOMPRESSED_INODES, "uncompressed inodes" }, - { SQFS_FLAG_UNCOMPRESSED_DATA, "uncompressed data" }, - { SQFS_FLAG_UNCOMPRESSED_FRAGMENTS, "uncompressed fragments" }, - { SQFS_FLAG_NO_FRAGMENTS, "no fragments" }, - { SQFS_FLAG_ALWAYS_FRAGMENTS, "always fragments" }, - { SQFS_FLAG_NO_DUPLICATES, "no duplicates" }, - { SQFS_FLAG_EXPORTABLE, "exportable" }, - { SQFS_FLAG_UNCOMPRESSED_XATTRS, "uncompressed xattrs" }, - { SQFS_FLAG_NO_XATTRS, "no xattrs" }, - { SQFS_FLAG_COMPRESSOR_OPTIONS, "compressor options" }, - { SQFS_FLAG_UNCOMPRESSED_IDS, "uncompressed ids" }, -}; - -static void print_value_difference(const char *name, sqfs_u64 a, sqfs_u64 b) -{ - sqfs_u64 diff; - char c; - - if (a != b) { - if (a < b) { - c = '+'; - diff = b - a; - } else { - c = '-'; - diff = a - b; - } - fprintf(stdout, "%s: %c%lu\n", name, c, - (unsigned long)diff); - } -} - -static void print_offset_diff(const char *name, sqfs_u64 a, sqfs_u64 b) -{ - if (a != b) - fprintf(stdout, "Location of %s differs\n", name); -} - -static void print_flag_diff(sqfs_u16 a, sqfs_u16 b) -{ - sqfs_u16 diff = a ^ b, mask; - size_t i; - char c; - - if (diff == 0) - return; - - fputs("flags:\n", stdout); - - for (i = 0; i < sizeof(sqfs_flags) / sizeof(sqfs_flags[0]); ++i) { - if (diff & sqfs_flags[i].mask) { - c = a & sqfs_flags[i].mask ? '<' : '>'; - - fprintf(stdout, "\t%c%s\n", c, sqfs_flags[i].name); - } - - a &= ~sqfs_flags[i].mask; - b &= ~sqfs_flags[i].mask; - diff &= ~sqfs_flags[i].mask; - } - - for (i = 0, mask = 0x01; i < 16; ++i, mask <<= 1) { - if (diff & mask) { - fprintf(stdout, "\t%c additional unknown\n", - a & mask ? '<' : '>'); - } - } -} - -int compare_super_blocks(const sqfs_super_t *a, const sqfs_super_t *b) -{ - if (memcmp(a, b, sizeof(*a)) == 0) - return 0; - - fputs("======== super blocks are different ========\n", stdout); - - /* TODO: if a new magic number or squashfs version is introduced, - compare them. */ - - print_value_difference("inode count", a->inode_count, b->inode_count); - print_value_difference("modification time", a->modification_time, - b->modification_time); - print_value_difference("block size", a->block_size, b->block_size); - print_value_difference("block log", a->block_log, b->block_log); - print_value_difference("fragment table entries", - a->fragment_entry_count, - b->fragment_entry_count); - print_value_difference("ID table entries", a->id_count, b->id_count); - - if (a->compression_id != b->compression_id) { - fprintf(stdout, "compressor: %s vs %s\n", - sqfs_compressor_name_from_id(a->compression_id), - sqfs_compressor_name_from_id(b->compression_id)); - } - - print_flag_diff(a->flags, b->flags); - - print_value_difference("total bytes used", a->bytes_used, - b->bytes_used); - - print_offset_diff("root inode", a->root_inode_ref, b->root_inode_ref); - print_offset_diff("ID table", a->id_table_start, b->id_table_start); - print_offset_diff("xattr ID table", a->xattr_id_table_start, - b->xattr_id_table_start); - print_offset_diff("inode table", a->inode_table_start, - b->inode_table_start); - print_offset_diff("directory table", a->directory_table_start, - b->directory_table_start); - print_offset_diff("fragment table", a->fragment_table_start, - b->fragment_table_start); - print_offset_diff("export table", a->export_table_start, - b->export_table_start); - return 1; -} diff --git a/bin/sqfsdiff/util.c b/bin/sqfsdiff/util.c deleted file mode 100644 index a11770f..0000000 --- a/bin/sqfsdiff/util.c +++ /dev/null @@ -1,27 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * util.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "sqfsdiff.h" - -char *node_path(const sqfs_tree_node_t *n) -{ - char *path; - int ret; - - ret = sqfs_tree_node_get_path(n, &path); - if (ret != 0) { - sqfs_perror(NULL, "get path", ret); - return NULL; - } - - if (canonicalize_name(path)) { - fprintf(stderr, "failed to canonicalization '%s'\n", path); - sqfs_free(path); - return NULL; - } - - return path; -} diff --git a/bin/tar2sqfs/Makemodule.am b/bin/tar2sqfs/Makemodule.am index faa2948..c8f52ea 100644 --- a/bin/tar2sqfs/Makemodule.am +++ b/bin/tar2sqfs/Makemodule.am @@ -1,5 +1,5 @@ -tar2sqfs_SOURCES = bin/tar2sqfs/tar2sqfs.c bin/tar2sqfs/tar2sqfs.h -tar2sqfs_SOURCES += bin/tar2sqfs/options.c bin/tar2sqfs/process_tarball.c +tar2sqfs_SOURCES = bin/tar2sqfs/src/tar2sqfs.c bin/tar2sqfs/src/tar2sqfs.h \ + bin/tar2sqfs/src/options.c bin/tar2sqfs/src/process_tarball.c tar2sqfs_CFLAGS = $(AM_CFLAGS) $(PTHREAD_CFLAGS) tar2sqfs_LDADD = libcommon.a libsquashfs.la libtar.a libio.a libxfrm.a tar2sqfs_LDADD += libfstree.a libcompat.a libfstree.a libutil.a $(LZO_LIBS) diff --git a/bin/tar2sqfs/options.c b/bin/tar2sqfs/options.c deleted file mode 100644 index f2185a6..0000000 --- a/bin/tar2sqfs/options.c +++ /dev/null @@ -1,257 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * options.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "tar2sqfs.h" - -static struct option long_opts[] = { - { "root-becomes", required_argument, NULL, 'r' }, - { "compressor", required_argument, NULL, 'c' }, - { "block-size", required_argument, NULL, 'b' }, - { "dev-block-size", required_argument, NULL, 'B' }, - { "defaults", required_argument, NULL, 'd' }, - { "num-jobs", required_argument, NULL, 'j' }, - { "queue-backlog", required_argument, NULL, 'Q' }, - { "comp-extra", required_argument, NULL, 'X' }, - { "no-skip", no_argument, NULL, 's' }, - { "no-xattr", no_argument, NULL, 'x' }, - { "no-keep-time", no_argument, NULL, 'k' }, - { "exportable", no_argument, NULL, 'e' }, - { "no-symlink-retarget", no_argument, NULL, 'S' }, - { "no-tail-packing", no_argument, NULL, 'T' }, - { "force", no_argument, NULL, 'f' }, - { "quiet", no_argument, NULL, 'q' }, - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, 'V' }, - { NULL, 0, NULL, 0 }, -}; - -static const char *short_opts = "r:c:b:B:d:X:j:Q:sxekfqSThV"; - -static const char *usagestr = -"Usage: tar2sqfs [OPTIONS...] \n" -"\n" -"Read a tar archive from stdin and turn it into a squashfs filesystem image.\n" -"\n" -"Possible options:\n" -"\n" -" --root-becomes, -r The specified directory becomes the root.\n" -" Only its children are packed into the image\n" -" and its attributes (ownership, permissions,\n" -" xattrs, ...) are stored in the root inode.\n" -" If not set and a tarbal has an entry for './'\n" -" or '/', it becomes the root instead.\n" -" --no-symlink-retarget, -S If --root-becomes is used, link targets are\n" -" adjusted if they are prefixed by the root\n" -" path. If this flag is set, symlinks are left\n" -" untouched and only hard links are changed.\n" -"\n" -" --compressor, -c Select the compressor to use.\n" -" A list of available compressors is below.\n" -" --comp-extra, -X A comma separated list of extra options for\n" -" the selected compressor. Specify 'help' to\n" -" get a list of available options.\n" -" --num-jobs, -j Number of compressor jobs to create.\n" -" --queue-backlog, -Q Maximum number of data blocks in the thread\n" -" worker queue before the packer starts waiting\n" -" for the block processors to catch up.\n" -" Defaults to 10 times the number of jobs.\n" -" --block-size, -b Block size to use for Squashfs image.\n" -" Defaults to %u.\n" -" --dev-block-size, -B Device block size to padd the image to.\n" -" Defaults to %u.\n" -" --defaults, -d A comma separated list of default values for\n" -" implicitly created directories.\n" -"\n" -" Possible options:\n" -" uid= 0 if not set.\n" -" gid= 0 if not set.\n" -" mode= 0755 if not set.\n" -" mtime= 0 if not set.\n" -"\n" -" --no-skip, -s Abort if a tar record cannot be read instead\n" -" of skipping it.\n" -" --no-xattr, -x Do not copy extended attributes from archive.\n" -" --no-keep-time, -k Do not keep the time stamps stored in the\n" -" archive. Instead, set defaults on all files.\n" -" --exportable, -e Generate an export table for NFS support.\n" -" --no-tail-packing, -T Do not perform tail end packing on files that\n" -" are larger than block size.\n" -" --force, -f Overwrite the output file if it exists.\n" -" --quiet, -q Do not print out progress reports.\n" -" --help, -h Print help text and exit.\n" -" --version, -V Print version information and exit.\n" -"\n"; - -bool dont_skip = false; -bool keep_time = true; -bool no_tail_pack = false; -bool no_symlink_retarget = false; -sqfs_writer_cfg_t cfg; -char *root_becomes = NULL; - -static void input_compressor_print_available(void) -{ - int i = XFRM_COMPRESSOR_MIN; - const char *name; - - fputs("\nSupported tar compression formats:\n", stdout); - - while (i <= XFRM_COMPRESSOR_MAX) { - name = xfrm_compressor_name_from_id(i); - - if (name != NULL) - printf("\t%s\n", name); - - ++i; - } - - fputs("\tuncompressed\n", stdout); - fputc('\n', stdout); -} - -void process_args(int argc, char **argv) -{ - bool have_compressor; - int i, ret; - - sqfs_writer_cfg_init(&cfg); - - for (;;) { - i = getopt_long(argc, argv, short_opts, long_opts, NULL); - if (i == -1) - break; - - switch (i) { - case 'S': - no_symlink_retarget = true; - break; - case 'T': - no_tail_pack = true; - break; - case 'b': - if (parse_size("Block size", &cfg.block_size, - optarg, 0)) { - exit(EXIT_FAILURE); - } - break; - case 'B': - if (parse_size("Device block size", &cfg.devblksize, - optarg, 0)) { - exit(EXIT_FAILURE); - } - if (cfg.devblksize < 1024) { - fputs("Device block size must be at " - "least 1024\n", stderr); - exit(EXIT_FAILURE); - } - break; - case 'c': - have_compressor = true; - ret = sqfs_compressor_id_from_name(optarg); - - if (ret < 0) { - have_compressor = false; -#ifdef WITH_LZO - if (cfg.comp_id == SQFS_COMP_LZO) - have_compressor = true; -#endif - } - - if (!have_compressor) { - fprintf(stderr, "Unsupported compressor '%s'\n", - optarg); - exit(EXIT_FAILURE); - } - - cfg.comp_id = ret; - break; - case 'j': - cfg.num_jobs = strtol(optarg, NULL, 0); - break; - case 'Q': - cfg.max_backlog = strtol(optarg, NULL, 0); - break; - case 'X': - cfg.comp_extra = optarg; - break; - case 'd': - cfg.fs_defaults = optarg; - break; - case 'x': - cfg.no_xattr = true; - break; - case 'k': - keep_time = false; - break; - case 'r': - free(root_becomes); - root_becomes = strdup(optarg); - if (root_becomes == NULL) { - perror("copying root directory name"); - exit(EXIT_FAILURE); - } - - if (canonicalize_name(root_becomes) != 0 || - strlen(root_becomes) == 0) { - fprintf(stderr, - "Invalid root directory '%s'.\n", - optarg); - goto fail_arg; - } - break; - case 's': - dont_skip = true; - break; - case 'e': - cfg.exportable = true; - break; - case 'f': - cfg.outmode |= SQFS_FILE_OPEN_OVERWRITE; - break; - case 'q': - cfg.quiet = true; - break; - case 'h': - printf(usagestr, SQFS_DEFAULT_BLOCK_SIZE, - SQFS_DEVBLK_SIZE); - compressor_print_available(); - input_compressor_print_available(); - exit(EXIT_SUCCESS); - case 'V': - print_version("tar2sqfs"); - exit(EXIT_SUCCESS); - default: - goto fail_arg; - } - } - - if (cfg.num_jobs < 1) - cfg.num_jobs = 1; - - if (cfg.max_backlog < 1) - cfg.max_backlog = 10 * cfg.num_jobs; - - if (cfg.comp_extra != NULL && strcmp(cfg.comp_extra, "help") == 0) { - compressor_print_help(cfg.comp_id); - exit(EXIT_SUCCESS); - } - - if (optind >= argc) { - fputs("Missing argument: squashfs image\n", stderr); - goto fail_arg; - } - - cfg.filename = argv[optind++]; - - if (optind < argc) { - fputs("Unknown extra arguments specified.\n", stderr); - goto fail_arg; - } - return; -fail_arg: - fputs("Try `tar2sqfs --help' for more information.\n", stderr); - exit(EXIT_FAILURE); -} diff --git a/bin/tar2sqfs/process_tarball.c b/bin/tar2sqfs/process_tarball.c deleted file mode 100644 index 6aaa24b..0000000 --- a/bin/tar2sqfs/process_tarball.c +++ /dev/null @@ -1,346 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * process_tarball.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "tar2sqfs.h" - -static int write_file(istream_t *input_file, sqfs_writer_t *sqfs, - const tar_header_decoded_t *hdr, - file_info_t *fi, sqfs_u64 filesize) -{ - const sparse_map_t *list; - int flags = 0, ret = 0; - sqfs_u64 offset, diff; - bool sparse_region; - ostream_t *out; - - if (no_tail_pack && filesize > cfg.block_size) - flags |= SQFS_BLK_DONT_FRAGMENT; - - out = data_writer_ostream_create(hdr->name, sqfs->data, &fi->inode, - flags); - - if (out == NULL) - return -1; - - list = hdr->sparse; - - for (offset = 0; offset < filesize; offset += diff) { - if (hdr->sparse != NULL) { - if (list == NULL) { - sparse_region = true; - diff = filesize - offset; - } else if (offset < list->offset) { - sparse_region = true; - diff = list->offset - offset; - } else if (offset - list->offset >= list->count) { - list = list->next; - diff = 0; - continue; - } else { - sparse_region = false; - diff = list->count - (offset - list->offset); - } - } else { - sparse_region = false; - diff = filesize - offset; - } - - if (diff > 0x7FFFFFFFUL) - diff = 0x7FFFFFFFUL; - - if (sparse_region) { - ret = ostream_append_sparse(out, diff); - } else { - ret = ostream_append_from_istream(out, input_file, - diff); - - if (ret == 0) { - fprintf(stderr, "%s: unexpected end-of-file\n", - hdr->name); - ret = -1; - } else if (ret > 0) { - diff = ret; - ret = 0; - } - } - - if (ret < 0) - break; - } - - ostream_flush(out); - sqfs_drop(out); - - if (ret) - return -1; - - return skip_padding(input_file, hdr->sparse == NULL ? - filesize : hdr->record_size); -} - -static int copy_xattr(sqfs_writer_t *sqfs, tree_node_t *node, - const tar_header_decoded_t *hdr) -{ - tar_xattr_t *xattr; - int ret; - - ret = sqfs_xattr_writer_begin(sqfs->xwr, 0); - if (ret) { - sqfs_perror(hdr->name, "beginning xattr block", ret); - return -1; - } - - for (xattr = hdr->xattr; xattr != NULL; xattr = xattr->next) { - if (sqfs_get_xattr_prefix_id(xattr->key) < 0) { - fprintf(stderr, "%s: squashfs does not " - "support xattr prefix of %s\n", - dont_skip ? "ERROR" : "WARNING", - xattr->key); - - if (dont_skip) - return -1; - continue; - } - - ret = sqfs_xattr_writer_add(sqfs->xwr, xattr->key, xattr->value, - xattr->value_len); - if (ret) { - sqfs_perror(hdr->name, "storing xattr key-value pair", - ret); - return -1; - } - } - - ret = sqfs_xattr_writer_end(sqfs->xwr, &node->xattr_idx); - if (ret) { - sqfs_perror(hdr->name, "completing xattr block", ret); - return -1; - } - - return 0; -} - -static int create_node_and_repack_data(istream_t *input_file, - sqfs_writer_t *sqfs, - tar_header_decoded_t *hdr) -{ - tree_node_t *node; - struct stat sb; - - if (hdr->is_hard_link) { - node = fstree_add_hard_link(&sqfs->fs, hdr->name, - hdr->link_target); - if (node == NULL) - goto fail_errno; - - if (!cfg.quiet) { - printf("Hard link %s -> %s\n", hdr->name, - hdr->link_target); - } - return 0; - } - - if (!keep_time) { - hdr->mtime = sqfs->fs.defaults.st_mtime; - } - - memset(&sb, 0, sizeof(sb)); - sb.st_mode = hdr->mode; - sb.st_uid = hdr->uid; - sb.st_gid = hdr->gid; - sb.st_rdev = hdr->devno; - sb.st_size = hdr->actual_size; - sb.st_mtime = hdr->mtime; - - node = fstree_add_generic(&sqfs->fs, hdr->name, - &sb, hdr->link_target); - if (node == NULL) - goto fail_errno; - - if (!cfg.quiet) - printf("Packing %s\n", hdr->name); - - if (!cfg.no_xattr) { - if (copy_xattr(sqfs, node, hdr)) - return -1; - } - - if (S_ISREG(hdr->mode)) { - if (write_file(input_file, sqfs, hdr, &node->data.file, - hdr->actual_size)) { - return -1; - } - } - - return 0; -fail_errno: - perror(hdr->name); - return -1; -} - -static int set_root_attribs(sqfs_writer_t *sqfs, - const tar_header_decoded_t *hdr) -{ - if (hdr->is_hard_link || !S_ISDIR(hdr->mode)) { - fprintf(stderr, "'%s' is not a directory!\n", hdr->name); - return -1; - } - - sqfs->fs.root->uid = hdr->uid; - sqfs->fs.root->gid = hdr->gid; - sqfs->fs.root->mode = hdr->mode; - - if (keep_time) - sqfs->fs.root->mod_time = hdr->mtime; - - if (!cfg.no_xattr) { - if (copy_xattr(sqfs, sqfs->fs.root, hdr)) - return -1; - } - - return 0; -} - -int process_tarball(istream_t *input_file, sqfs_writer_t *sqfs) -{ - bool skip, is_root, is_prefixed; - tar_header_decoded_t hdr; - sqfs_u64 offset, count; - sparse_map_t *m; - size_t rootlen; - char *target; - int ret; - - rootlen = root_becomes == NULL ? 0 : strlen(root_becomes); - - for (;;) { - ret = read_header(input_file, &hdr); - if (ret > 0) - break; - if (ret < 0) - return -1; - - if (hdr.mtime < 0) - hdr.mtime = 0; - - if ((sqfs_u64)hdr.mtime > 0x0FFFFFFFFUL) - hdr.mtime = 0x0FFFFFFFFUL; - - skip = false; - is_root = false; - is_prefixed = true; - - if (hdr.name == NULL || canonicalize_name(hdr.name) != 0) { - fprintf(stderr, "skipping '%s' (invalid name)\n", - hdr.name); - skip = true; - } else if (root_becomes != NULL) { - if (strncmp(hdr.name, root_becomes, rootlen) == 0) { - if (hdr.name[rootlen] == '\0') { - is_root = true; - } else if (hdr.name[rootlen] != '/') { - is_prefixed = false; - } - } else { - is_prefixed = false; - } - - if (is_prefixed && !is_root) { - memmove(hdr.name, hdr.name + rootlen + 1, - strlen(hdr.name + rootlen + 1) + 1); - } - - if (is_prefixed && hdr.name[0] == '\0') { - fputs("skipping entry with empty name\n", - stderr); - skip = true; - } - - if (hdr.link_target != NULL && - (hdr.is_hard_link || !no_symlink_retarget)) { - target = strdup(hdr.link_target); - if (target == NULL) { - fprintf(stderr, "packing '%s': %s\n", - hdr.name, strerror(errno)); - goto fail; - } - - if (canonicalize_name(target) == 0 && - !strncmp(target, root_becomes, rootlen) && - target[rootlen] == '/') { - memmove(hdr.link_target, - target + rootlen, - strlen(target + rootlen) + 1); - } - - free(target); - } - } else if (hdr.name[0] == '\0') { - is_root = true; - } - - if (!is_prefixed) { - if (skip_entry(input_file, hdr.record_size)) - goto fail; - clear_header(&hdr); - continue; - } - - if (is_root) { - if (set_root_attribs(sqfs, &hdr)) - goto fail; - clear_header(&hdr); - continue; - } - - if (!skip && hdr.unknown_record) { - fprintf(stderr, "%s: unknown entry type\n", hdr.name); - skip = true; - } - - if (!skip && hdr.sparse != NULL) { - offset = hdr.sparse->offset; - count = 0; - - for (m = hdr.sparse; m != NULL; m = m->next) { - if (m->offset < offset) { - skip = true; - break; - } - offset = m->offset + m->count; - count += m->count; - } - - if (count != hdr.record_size) - skip = true; - - if (skip) { - fprintf(stderr, "%s: broken sparse " - "file layout\n", hdr.name); - } - } - - if (skip) { - if (dont_skip) - goto fail; - if (skip_entry(input_file, hdr.record_size)) - goto fail; - - clear_header(&hdr); - continue; - } - - if (create_node_and_repack_data(input_file, sqfs, &hdr)) - goto fail; - - clear_header(&hdr); - } - - return 0; -fail: - clear_header(&hdr); - return -1; -} diff --git a/bin/tar2sqfs/src/options.c b/bin/tar2sqfs/src/options.c new file mode 100644 index 0000000..f2185a6 --- /dev/null +++ b/bin/tar2sqfs/src/options.c @@ -0,0 +1,257 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * options.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "tar2sqfs.h" + +static struct option long_opts[] = { + { "root-becomes", required_argument, NULL, 'r' }, + { "compressor", required_argument, NULL, 'c' }, + { "block-size", required_argument, NULL, 'b' }, + { "dev-block-size", required_argument, NULL, 'B' }, + { "defaults", required_argument, NULL, 'd' }, + { "num-jobs", required_argument, NULL, 'j' }, + { "queue-backlog", required_argument, NULL, 'Q' }, + { "comp-extra", required_argument, NULL, 'X' }, + { "no-skip", no_argument, NULL, 's' }, + { "no-xattr", no_argument, NULL, 'x' }, + { "no-keep-time", no_argument, NULL, 'k' }, + { "exportable", no_argument, NULL, 'e' }, + { "no-symlink-retarget", no_argument, NULL, 'S' }, + { "no-tail-packing", no_argument, NULL, 'T' }, + { "force", no_argument, NULL, 'f' }, + { "quiet", no_argument, NULL, 'q' }, + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, 'V' }, + { NULL, 0, NULL, 0 }, +}; + +static const char *short_opts = "r:c:b:B:d:X:j:Q:sxekfqSThV"; + +static const char *usagestr = +"Usage: tar2sqfs [OPTIONS...] \n" +"\n" +"Read a tar archive from stdin and turn it into a squashfs filesystem image.\n" +"\n" +"Possible options:\n" +"\n" +" --root-becomes, -r The specified directory becomes the root.\n" +" Only its children are packed into the image\n" +" and its attributes (ownership, permissions,\n" +" xattrs, ...) are stored in the root inode.\n" +" If not set and a tarbal has an entry for './'\n" +" or '/', it becomes the root instead.\n" +" --no-symlink-retarget, -S If --root-becomes is used, link targets are\n" +" adjusted if they are prefixed by the root\n" +" path. If this flag is set, symlinks are left\n" +" untouched and only hard links are changed.\n" +"\n" +" --compressor, -c Select the compressor to use.\n" +" A list of available compressors is below.\n" +" --comp-extra, -X A comma separated list of extra options for\n" +" the selected compressor. Specify 'help' to\n" +" get a list of available options.\n" +" --num-jobs, -j Number of compressor jobs to create.\n" +" --queue-backlog, -Q Maximum number of data blocks in the thread\n" +" worker queue before the packer starts waiting\n" +" for the block processors to catch up.\n" +" Defaults to 10 times the number of jobs.\n" +" --block-size, -b Block size to use for Squashfs image.\n" +" Defaults to %u.\n" +" --dev-block-size, -B Device block size to padd the image to.\n" +" Defaults to %u.\n" +" --defaults, -d A comma separated list of default values for\n" +" implicitly created directories.\n" +"\n" +" Possible options:\n" +" uid= 0 if not set.\n" +" gid= 0 if not set.\n" +" mode= 0755 if not set.\n" +" mtime= 0 if not set.\n" +"\n" +" --no-skip, -s Abort if a tar record cannot be read instead\n" +" of skipping it.\n" +" --no-xattr, -x Do not copy extended attributes from archive.\n" +" --no-keep-time, -k Do not keep the time stamps stored in the\n" +" archive. Instead, set defaults on all files.\n" +" --exportable, -e Generate an export table for NFS support.\n" +" --no-tail-packing, -T Do not perform tail end packing on files that\n" +" are larger than block size.\n" +" --force, -f Overwrite the output file if it exists.\n" +" --quiet, -q Do not print out progress reports.\n" +" --help, -h Print help text and exit.\n" +" --version, -V Print version information and exit.\n" +"\n"; + +bool dont_skip = false; +bool keep_time = true; +bool no_tail_pack = false; +bool no_symlink_retarget = false; +sqfs_writer_cfg_t cfg; +char *root_becomes = NULL; + +static void input_compressor_print_available(void) +{ + int i = XFRM_COMPRESSOR_MIN; + const char *name; + + fputs("\nSupported tar compression formats:\n", stdout); + + while (i <= XFRM_COMPRESSOR_MAX) { + name = xfrm_compressor_name_from_id(i); + + if (name != NULL) + printf("\t%s\n", name); + + ++i; + } + + fputs("\tuncompressed\n", stdout); + fputc('\n', stdout); +} + +void process_args(int argc, char **argv) +{ + bool have_compressor; + int i, ret; + + sqfs_writer_cfg_init(&cfg); + + for (;;) { + i = getopt_long(argc, argv, short_opts, long_opts, NULL); + if (i == -1) + break; + + switch (i) { + case 'S': + no_symlink_retarget = true; + break; + case 'T': + no_tail_pack = true; + break; + case 'b': + if (parse_size("Block size", &cfg.block_size, + optarg, 0)) { + exit(EXIT_FAILURE); + } + break; + case 'B': + if (parse_size("Device block size", &cfg.devblksize, + optarg, 0)) { + exit(EXIT_FAILURE); + } + if (cfg.devblksize < 1024) { + fputs("Device block size must be at " + "least 1024\n", stderr); + exit(EXIT_FAILURE); + } + break; + case 'c': + have_compressor = true; + ret = sqfs_compressor_id_from_name(optarg); + + if (ret < 0) { + have_compressor = false; +#ifdef WITH_LZO + if (cfg.comp_id == SQFS_COMP_LZO) + have_compressor = true; +#endif + } + + if (!have_compressor) { + fprintf(stderr, "Unsupported compressor '%s'\n", + optarg); + exit(EXIT_FAILURE); + } + + cfg.comp_id = ret; + break; + case 'j': + cfg.num_jobs = strtol(optarg, NULL, 0); + break; + case 'Q': + cfg.max_backlog = strtol(optarg, NULL, 0); + break; + case 'X': + cfg.comp_extra = optarg; + break; + case 'd': + cfg.fs_defaults = optarg; + break; + case 'x': + cfg.no_xattr = true; + break; + case 'k': + keep_time = false; + break; + case 'r': + free(root_becomes); + root_becomes = strdup(optarg); + if (root_becomes == NULL) { + perror("copying root directory name"); + exit(EXIT_FAILURE); + } + + if (canonicalize_name(root_becomes) != 0 || + strlen(root_becomes) == 0) { + fprintf(stderr, + "Invalid root directory '%s'.\n", + optarg); + goto fail_arg; + } + break; + case 's': + dont_skip = true; + break; + case 'e': + cfg.exportable = true; + break; + case 'f': + cfg.outmode |= SQFS_FILE_OPEN_OVERWRITE; + break; + case 'q': + cfg.quiet = true; + break; + case 'h': + printf(usagestr, SQFS_DEFAULT_BLOCK_SIZE, + SQFS_DEVBLK_SIZE); + compressor_print_available(); + input_compressor_print_available(); + exit(EXIT_SUCCESS); + case 'V': + print_version("tar2sqfs"); + exit(EXIT_SUCCESS); + default: + goto fail_arg; + } + } + + if (cfg.num_jobs < 1) + cfg.num_jobs = 1; + + if (cfg.max_backlog < 1) + cfg.max_backlog = 10 * cfg.num_jobs; + + if (cfg.comp_extra != NULL && strcmp(cfg.comp_extra, "help") == 0) { + compressor_print_help(cfg.comp_id); + exit(EXIT_SUCCESS); + } + + if (optind >= argc) { + fputs("Missing argument: squashfs image\n", stderr); + goto fail_arg; + } + + cfg.filename = argv[optind++]; + + if (optind < argc) { + fputs("Unknown extra arguments specified.\n", stderr); + goto fail_arg; + } + return; +fail_arg: + fputs("Try `tar2sqfs --help' for more information.\n", stderr); + exit(EXIT_FAILURE); +} diff --git a/bin/tar2sqfs/src/process_tarball.c b/bin/tar2sqfs/src/process_tarball.c new file mode 100644 index 0000000..6aaa24b --- /dev/null +++ b/bin/tar2sqfs/src/process_tarball.c @@ -0,0 +1,346 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * process_tarball.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "tar2sqfs.h" + +static int write_file(istream_t *input_file, sqfs_writer_t *sqfs, + const tar_header_decoded_t *hdr, + file_info_t *fi, sqfs_u64 filesize) +{ + const sparse_map_t *list; + int flags = 0, ret = 0; + sqfs_u64 offset, diff; + bool sparse_region; + ostream_t *out; + + if (no_tail_pack && filesize > cfg.block_size) + flags |= SQFS_BLK_DONT_FRAGMENT; + + out = data_writer_ostream_create(hdr->name, sqfs->data, &fi->inode, + flags); + + if (out == NULL) + return -1; + + list = hdr->sparse; + + for (offset = 0; offset < filesize; offset += diff) { + if (hdr->sparse != NULL) { + if (list == NULL) { + sparse_region = true; + diff = filesize - offset; + } else if (offset < list->offset) { + sparse_region = true; + diff = list->offset - offset; + } else if (offset - list->offset >= list->count) { + list = list->next; + diff = 0; + continue; + } else { + sparse_region = false; + diff = list->count - (offset - list->offset); + } + } else { + sparse_region = false; + diff = filesize - offset; + } + + if (diff > 0x7FFFFFFFUL) + diff = 0x7FFFFFFFUL; + + if (sparse_region) { + ret = ostream_append_sparse(out, diff); + } else { + ret = ostream_append_from_istream(out, input_file, + diff); + + if (ret == 0) { + fprintf(stderr, "%s: unexpected end-of-file\n", + hdr->name); + ret = -1; + } else if (ret > 0) { + diff = ret; + ret = 0; + } + } + + if (ret < 0) + break; + } + + ostream_flush(out); + sqfs_drop(out); + + if (ret) + return -1; + + return skip_padding(input_file, hdr->sparse == NULL ? + filesize : hdr->record_size); +} + +static int copy_xattr(sqfs_writer_t *sqfs, tree_node_t *node, + const tar_header_decoded_t *hdr) +{ + tar_xattr_t *xattr; + int ret; + + ret = sqfs_xattr_writer_begin(sqfs->xwr, 0); + if (ret) { + sqfs_perror(hdr->name, "beginning xattr block", ret); + return -1; + } + + for (xattr = hdr->xattr; xattr != NULL; xattr = xattr->next) { + if (sqfs_get_xattr_prefix_id(xattr->key) < 0) { + fprintf(stderr, "%s: squashfs does not " + "support xattr prefix of %s\n", + dont_skip ? "ERROR" : "WARNING", + xattr->key); + + if (dont_skip) + return -1; + continue; + } + + ret = sqfs_xattr_writer_add(sqfs->xwr, xattr->key, xattr->value, + xattr->value_len); + if (ret) { + sqfs_perror(hdr->name, "storing xattr key-value pair", + ret); + return -1; + } + } + + ret = sqfs_xattr_writer_end(sqfs->xwr, &node->xattr_idx); + if (ret) { + sqfs_perror(hdr->name, "completing xattr block", ret); + return -1; + } + + return 0; +} + +static int create_node_and_repack_data(istream_t *input_file, + sqfs_writer_t *sqfs, + tar_header_decoded_t *hdr) +{ + tree_node_t *node; + struct stat sb; + + if (hdr->is_hard_link) { + node = fstree_add_hard_link(&sqfs->fs, hdr->name, + hdr->link_target); + if (node == NULL) + goto fail_errno; + + if (!cfg.quiet) { + printf("Hard link %s -> %s\n", hdr->name, + hdr->link_target); + } + return 0; + } + + if (!keep_time) { + hdr->mtime = sqfs->fs.defaults.st_mtime; + } + + memset(&sb, 0, sizeof(sb)); + sb.st_mode = hdr->mode; + sb.st_uid = hdr->uid; + sb.st_gid = hdr->gid; + sb.st_rdev = hdr->devno; + sb.st_size = hdr->actual_size; + sb.st_mtime = hdr->mtime; + + node = fstree_add_generic(&sqfs->fs, hdr->name, + &sb, hdr->link_target); + if (node == NULL) + goto fail_errno; + + if (!cfg.quiet) + printf("Packing %s\n", hdr->name); + + if (!cfg.no_xattr) { + if (copy_xattr(sqfs, node, hdr)) + return -1; + } + + if (S_ISREG(hdr->mode)) { + if (write_file(input_file, sqfs, hdr, &node->data.file, + hdr->actual_size)) { + return -1; + } + } + + return 0; +fail_errno: + perror(hdr->name); + return -1; +} + +static int set_root_attribs(sqfs_writer_t *sqfs, + const tar_header_decoded_t *hdr) +{ + if (hdr->is_hard_link || !S_ISDIR(hdr->mode)) { + fprintf(stderr, "'%s' is not a directory!\n", hdr->name); + return -1; + } + + sqfs->fs.root->uid = hdr->uid; + sqfs->fs.root->gid = hdr->gid; + sqfs->fs.root->mode = hdr->mode; + + if (keep_time) + sqfs->fs.root->mod_time = hdr->mtime; + + if (!cfg.no_xattr) { + if (copy_xattr(sqfs, sqfs->fs.root, hdr)) + return -1; + } + + return 0; +} + +int process_tarball(istream_t *input_file, sqfs_writer_t *sqfs) +{ + bool skip, is_root, is_prefixed; + tar_header_decoded_t hdr; + sqfs_u64 offset, count; + sparse_map_t *m; + size_t rootlen; + char *target; + int ret; + + rootlen = root_becomes == NULL ? 0 : strlen(root_becomes); + + for (;;) { + ret = read_header(input_file, &hdr); + if (ret > 0) + break; + if (ret < 0) + return -1; + + if (hdr.mtime < 0) + hdr.mtime = 0; + + if ((sqfs_u64)hdr.mtime > 0x0FFFFFFFFUL) + hdr.mtime = 0x0FFFFFFFFUL; + + skip = false; + is_root = false; + is_prefixed = true; + + if (hdr.name == NULL || canonicalize_name(hdr.name) != 0) { + fprintf(stderr, "skipping '%s' (invalid name)\n", + hdr.name); + skip = true; + } else if (root_becomes != NULL) { + if (strncmp(hdr.name, root_becomes, rootlen) == 0) { + if (hdr.name[rootlen] == '\0') { + is_root = true; + } else if (hdr.name[rootlen] != '/') { + is_prefixed = false; + } + } else { + is_prefixed = false; + } + + if (is_prefixed && !is_root) { + memmove(hdr.name, hdr.name + rootlen + 1, + strlen(hdr.name + rootlen + 1) + 1); + } + + if (is_prefixed && hdr.name[0] == '\0') { + fputs("skipping entry with empty name\n", + stderr); + skip = true; + } + + if (hdr.link_target != NULL && + (hdr.is_hard_link || !no_symlink_retarget)) { + target = strdup(hdr.link_target); + if (target == NULL) { + fprintf(stderr, "packing '%s': %s\n", + hdr.name, strerror(errno)); + goto fail; + } + + if (canonicalize_name(target) == 0 && + !strncmp(target, root_becomes, rootlen) && + target[rootlen] == '/') { + memmove(hdr.link_target, + target + rootlen, + strlen(target + rootlen) + 1); + } + + free(target); + } + } else if (hdr.name[0] == '\0') { + is_root = true; + } + + if (!is_prefixed) { + if (skip_entry(input_file, hdr.record_size)) + goto fail; + clear_header(&hdr); + continue; + } + + if (is_root) { + if (set_root_attribs(sqfs, &hdr)) + goto fail; + clear_header(&hdr); + continue; + } + + if (!skip && hdr.unknown_record) { + fprintf(stderr, "%s: unknown entry type\n", hdr.name); + skip = true; + } + + if (!skip && hdr.sparse != NULL) { + offset = hdr.sparse->offset; + count = 0; + + for (m = hdr.sparse; m != NULL; m = m->next) { + if (m->offset < offset) { + skip = true; + break; + } + offset = m->offset + m->count; + count += m->count; + } + + if (count != hdr.record_size) + skip = true; + + if (skip) { + fprintf(stderr, "%s: broken sparse " + "file layout\n", hdr.name); + } + } + + if (skip) { + if (dont_skip) + goto fail; + if (skip_entry(input_file, hdr.record_size)) + goto fail; + + clear_header(&hdr); + continue; + } + + if (create_node_and_repack_data(input_file, sqfs, &hdr)) + goto fail; + + clear_header(&hdr); + } + + return 0; +fail: + clear_header(&hdr); + return -1; +} diff --git a/bin/tar2sqfs/src/tar2sqfs.c b/bin/tar2sqfs/src/tar2sqfs.c new file mode 100644 index 0000000..9257fed --- /dev/null +++ b/bin/tar2sqfs/src/tar2sqfs.c @@ -0,0 +1,104 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * tar2sqfs.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "tar2sqfs.h" + +static int tar_probe(const sqfs_u8 *data, size_t size) +{ + size_t i, offset; + + if (size >= TAR_RECORD_SIZE) { + for (i = 0; i < TAR_RECORD_SIZE; ++i) { + if (data[i] != 0x00) + break; + } + + if (i == TAR_RECORD_SIZE) { + data += TAR_RECORD_SIZE; + size -= TAR_RECORD_SIZE; + } + } + + offset = offsetof(tar_header_t, magic); + + if (offset + 5 <= size) { + if (memcmp(data + offset, "ustar", 5) == 0) + return 1; + } + + return 0; +} + +static istream_t *magic_autowrap(istream_t *strm) +{ + xfrm_stream_t *xfrm = NULL; + istream_t *wrapper = NULL; + const sqfs_u8 *data; + size_t avail; + int ret; + + ret = istream_precache(strm); + if (ret != 0) + goto out; + + data = strm->buffer + strm->buffer_offset; + avail = strm->buffer_used - strm->buffer_offset; + + ret = tar_probe(data, avail); + if (ret > 0) + return strm; + + ret = xfrm_compressor_id_from_magic(data, avail); + if (ret <= 0) + return strm; + + xfrm = decompressor_stream_create(ret); + if (xfrm == NULL) + goto out; + + wrapper = istream_xfrm_create(strm, xfrm); +out: + sqfs_drop(strm); + sqfs_drop(xfrm); + return wrapper; +} + +int main(int argc, char **argv) +{ + int status = EXIT_FAILURE; + istream_t *input_file = NULL; + sqfs_writer_t sqfs; + + process_args(argc, argv); + + input_file = istream_open_stdin(); + if (input_file == NULL) + return EXIT_FAILURE; + + input_file = magic_autowrap(input_file); + if (input_file == NULL) + return EXIT_FAILURE; + + memset(&sqfs, 0, sizeof(sqfs)); + if (sqfs_writer_init(&sqfs, &cfg)) + goto out_if; + + if (process_tarball(input_file, &sqfs)) + goto out; + + if (fstree_post_process(&sqfs.fs)) + goto out; + + if (sqfs_writer_finish(&sqfs, &cfg)) + goto out; + + status = EXIT_SUCCESS; +out: + sqfs_writer_cleanup(&sqfs, status); +out_if: + sqfs_drop(input_file); + return status; +} diff --git a/bin/tar2sqfs/src/tar2sqfs.h b/bin/tar2sqfs/src/tar2sqfs.h new file mode 100644 index 0000000..a21774b --- /dev/null +++ b/bin/tar2sqfs/src/tar2sqfs.h @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * tar2sqfs.h + * + * Copyright (C) 2019 David Oberhollenzer + */ +#ifndef TAR2SQFS_H +#define TAR2SQFS_H + +#include "config.h" +#include "common.h" +#include "compat.h" + +#include "util/util.h" +#include "tar/tar.h" +#include "tar/format.h" +#include "xfrm/compress.h" +#include "io/xfrm.h" + +#include +#include +#include +#include +#include + +/* options.c */ +extern bool dont_skip; +extern bool keep_time; +extern bool no_tail_pack; +extern bool no_symlink_retarget; +extern sqfs_writer_cfg_t cfg; +extern char *root_becomes; + +void process_args(int argc, char **argv); + +/* process_tarball.c */ +int process_tarball(istream_t *input_file, sqfs_writer_t *sqfs); + +#endif /* TAR2SQFS_H */ diff --git a/bin/tar2sqfs/tar2sqfs.c b/bin/tar2sqfs/tar2sqfs.c deleted file mode 100644 index 9257fed..0000000 --- a/bin/tar2sqfs/tar2sqfs.c +++ /dev/null @@ -1,104 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * tar2sqfs.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "tar2sqfs.h" - -static int tar_probe(const sqfs_u8 *data, size_t size) -{ - size_t i, offset; - - if (size >= TAR_RECORD_SIZE) { - for (i = 0; i < TAR_RECORD_SIZE; ++i) { - if (data[i] != 0x00) - break; - } - - if (i == TAR_RECORD_SIZE) { - data += TAR_RECORD_SIZE; - size -= TAR_RECORD_SIZE; - } - } - - offset = offsetof(tar_header_t, magic); - - if (offset + 5 <= size) { - if (memcmp(data + offset, "ustar", 5) == 0) - return 1; - } - - return 0; -} - -static istream_t *magic_autowrap(istream_t *strm) -{ - xfrm_stream_t *xfrm = NULL; - istream_t *wrapper = NULL; - const sqfs_u8 *data; - size_t avail; - int ret; - - ret = istream_precache(strm); - if (ret != 0) - goto out; - - data = strm->buffer + strm->buffer_offset; - avail = strm->buffer_used - strm->buffer_offset; - - ret = tar_probe(data, avail); - if (ret > 0) - return strm; - - ret = xfrm_compressor_id_from_magic(data, avail); - if (ret <= 0) - return strm; - - xfrm = decompressor_stream_create(ret); - if (xfrm == NULL) - goto out; - - wrapper = istream_xfrm_create(strm, xfrm); -out: - sqfs_drop(strm); - sqfs_drop(xfrm); - return wrapper; -} - -int main(int argc, char **argv) -{ - int status = EXIT_FAILURE; - istream_t *input_file = NULL; - sqfs_writer_t sqfs; - - process_args(argc, argv); - - input_file = istream_open_stdin(); - if (input_file == NULL) - return EXIT_FAILURE; - - input_file = magic_autowrap(input_file); - if (input_file == NULL) - return EXIT_FAILURE; - - memset(&sqfs, 0, sizeof(sqfs)); - if (sqfs_writer_init(&sqfs, &cfg)) - goto out_if; - - if (process_tarball(input_file, &sqfs)) - goto out; - - if (fstree_post_process(&sqfs.fs)) - goto out; - - if (sqfs_writer_finish(&sqfs, &cfg)) - goto out; - - status = EXIT_SUCCESS; -out: - sqfs_writer_cleanup(&sqfs, status); -out_if: - sqfs_drop(input_file); - return status; -} diff --git a/bin/tar2sqfs/tar2sqfs.h b/bin/tar2sqfs/tar2sqfs.h deleted file mode 100644 index a21774b..0000000 --- a/bin/tar2sqfs/tar2sqfs.h +++ /dev/null @@ -1,39 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * tar2sqfs.h - * - * Copyright (C) 2019 David Oberhollenzer - */ -#ifndef TAR2SQFS_H -#define TAR2SQFS_H - -#include "config.h" -#include "common.h" -#include "compat.h" - -#include "util/util.h" -#include "tar/tar.h" -#include "tar/format.h" -#include "xfrm/compress.h" -#include "io/xfrm.h" - -#include -#include -#include -#include -#include - -/* options.c */ -extern bool dont_skip; -extern bool keep_time; -extern bool no_tail_pack; -extern bool no_symlink_retarget; -extern sqfs_writer_cfg_t cfg; -extern char *root_becomes; - -void process_args(int argc, char **argv); - -/* process_tarball.c */ -int process_tarball(istream_t *input_file, sqfs_writer_t *sqfs); - -#endif /* TAR2SQFS_H */ diff --git a/lib/common/Makemodule.am b/lib/common/Makemodule.am index b19ecd6..74b924e 100644 --- a/lib/common/Makemodule.am +++ b/lib/common/Makemodule.am @@ -1,18 +1,16 @@ -libcommon_a_SOURCES = lib/common/hardlink.c -libcommon_a_SOURCES += lib/common/print_version.c lib/common/data_reader_dump.c -libcommon_a_SOURCES += lib/common/compress.c lib/common/comp_opt.c -libcommon_a_SOURCES += lib/common/data_writer.c include/common.h -libcommon_a_SOURCES += lib/common/data_writer_ostream.c -libcommon_a_SOURCES += lib/common/perror.c lib/common/parse_size.c -libcommon_a_SOURCES += lib/common/print_size.c include/simple_writer.h -libcommon_a_SOURCES += include/compress_cli.h -libcommon_a_SOURCES += lib/common/writer/init.c lib/common/writer/cleanup.c -libcommon_a_SOURCES += lib/common/writer/serialize_fstree.c -libcommon_a_SOURCES += lib/common/writer/finish.c +libcommon_a_SOURCES = include/common.h include/simple_writer.h \ + include/compress_cli.h \ + lib/common/src/hardlink.c lib/common/src/print_version.c \ + lib/common/src/data_reader_dump.c lib/common/src/compress.c \ + lib/common/src/comp_opt.c lib/common/src/data_writer.c \ + lib/common/src/data_writer_ostream.c lib/common/src/perror.c \ + lib/common/src/parse_size.c lib/common/src/print_size.c \ + lib/common/src/writer/init.c lib/common/src/writer/cleanup.c \ + lib/common/src/writer/serialize_fstree.c lib/common/src/writer/finish.c libcommon_a_CFLAGS = $(AM_CFLAGS) $(LZO_CFLAGS) if WITH_LZO -libcommon_a_SOURCES += lib/common/comp_lzo.c +libcommon_a_SOURCES += lib/common/src/comp_lzo.c endif noinst_LIBRARIES += libcommon.a diff --git a/lib/common/comp_lzo.c b/lib/common/comp_lzo.c deleted file mode 100644 index 2021d34..0000000 --- a/lib/common/comp_lzo.c +++ /dev/null @@ -1,285 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * comp_lzo.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "config.h" -#include "compress_cli.h" -#include "compat.h" - -#include -#include -#include - -#include - -#define LZO_MAX_SIZE(size) (size + (size / 16) + 64 + 3) - -#define LZO_NUM_ALGS (sizeof(lzo_algs) / sizeof(lzo_algs[0])) - -typedef int (*lzo_cb_t)(const lzo_bytep src, lzo_uint src_len, lzo_bytep dst, - lzo_uintp dst_len, lzo_voidp wrkmem); - -static const struct { - lzo_cb_t compress; - size_t bufsize; -} lzo_algs[] = { - [SQFS_LZO1X_1] = { - .compress = lzo1x_1_compress, - .bufsize = LZO1X_1_MEM_COMPRESS, - }, - [SQFS_LZO1X_1_11] = { - .compress = lzo1x_1_11_compress, - .bufsize = LZO1X_1_11_MEM_COMPRESS, - }, - [SQFS_LZO1X_1_12] = { - .compress = lzo1x_1_12_compress, - .bufsize = LZO1X_1_12_MEM_COMPRESS, - }, - [SQFS_LZO1X_1_15] = { - .compress = lzo1x_1_15_compress, - .bufsize = LZO1X_1_15_MEM_COMPRESS, - }, - [SQFS_LZO1X_999] = { - .compress = lzo1x_999_compress, - .bufsize = LZO1X_999_MEM_COMPRESS, - }, -}; - -typedef struct { - sqfs_compressor_t base; - size_t block_size; - int algorithm; - int level; - - size_t buf_size; - size_t work_size; - - sqfs_u8 buffer[]; -} lzo_compressor_t; - -typedef struct { - sqfs_u32 algorithm; - sqfs_u32 level; -} lzo_options_t; - -static int lzo_write_options(sqfs_compressor_t *base, sqfs_file_t *file) -{ - lzo_compressor_t *lzo = (lzo_compressor_t *)base; - sqfs_u8 buffer[sizeof(lzo_options_t) + 2]; - lzo_options_t opt; - sqfs_u16 header; - int ret; - - if (lzo->algorithm == SQFS_LZO_DEFAULT_ALG && - lzo->level == SQFS_LZO_DEFAULT_LEVEL) { - return 0; - } - - opt.algorithm = htole32(lzo->algorithm); - - if (lzo->algorithm == SQFS_LZO1X_999) { - opt.level = htole32(lzo->level); - } else { - opt.level = 0; - } - - header = htole16(0x8000 | sizeof(opt)); - - memcpy(buffer, &header, sizeof(header)); - memcpy(buffer + 2, &opt, sizeof(opt)); - - ret = file->write_at(file, sizeof(sqfs_super_t), - buffer, sizeof(buffer)); - - return ret ? ret : (int)sizeof(buffer); -} - -static int lzo_read_options(sqfs_compressor_t *base, sqfs_file_t *file) -{ - lzo_compressor_t *lzo = (lzo_compressor_t *)base; - sqfs_u8 buffer[sizeof(lzo_options_t) + 2]; - lzo_options_t opt; - sqfs_u16 header; - int ret; - - ret = file->read_at(file, sizeof(sqfs_super_t), - buffer, sizeof(buffer)); - if (ret) - return ret; - - memcpy(&header, buffer, sizeof(header)); - if (le16toh(header) != (0x8000 | sizeof(opt))) - return SQFS_ERROR_CORRUPTED; - - memcpy(&opt, buffer + 2, sizeof(opt)); - lzo->algorithm = le32toh(opt.algorithm); - lzo->level = le32toh(opt.level); - - switch(lzo->algorithm) { - case SQFS_LZO1X_1: - case SQFS_LZO1X_1_11: - case SQFS_LZO1X_1_12: - case SQFS_LZO1X_1_15: - if (lzo->level != 0) - return SQFS_ERROR_UNSUPPORTED; - break; - case SQFS_LZO1X_999: - if (lzo->level < 1 || lzo->level > 9) - return SQFS_ERROR_UNSUPPORTED; - break; - default: - return SQFS_ERROR_UNSUPPORTED; - } - - return 0; -} - -static sqfs_s32 lzo_comp_block(sqfs_compressor_t *base, const sqfs_u8 *in, - sqfs_u32 size, sqfs_u8 *out, sqfs_u32 outsize) -{ - lzo_compressor_t *lzo = (lzo_compressor_t *)base; - void *scratch; - lzo_uint len; - int ret; - - if (size >= 0x7FFFFFFF) - return 0; - - scratch = lzo->buffer + lzo->work_size; - len = lzo->buf_size - lzo->work_size; - - if (lzo->algorithm == SQFS_LZO1X_999 && - lzo->level != SQFS_LZO_DEFAULT_LEVEL) { - ret = lzo1x_999_compress_level(in, size, scratch, &len, - lzo->buffer, NULL, 0, 0, - lzo->level); - } else { - ret = lzo_algs[lzo->algorithm].compress(in, size, scratch, - &len, lzo->buffer); - } - - if (ret != LZO_E_OK) - return SQFS_ERROR_COMPRESSOR; - - if (len < size && len <= outsize) { - memcpy(out, scratch, len); - return len; - } - - return 0; -} - -static sqfs_s32 lzo_uncomp_block(sqfs_compressor_t *base, const sqfs_u8 *in, - sqfs_u32 size, sqfs_u8 *out, sqfs_u32 outsize) -{ - lzo_compressor_t *lzo = (lzo_compressor_t *)base; - lzo_uint len = outsize; - int ret; - - if (outsize >= 0x7FFFFFFF) - return 0; - - ret = lzo1x_decompress_safe(in, size, out, &len, lzo->buffer); - - if (ret != LZO_E_OK) - return SQFS_ERROR_COMPRESSOR; - - return len; -} - -static void lzo_get_configuration(const sqfs_compressor_t *base, - sqfs_compressor_config_t *cfg) -{ - const lzo_compressor_t *lzo = (const lzo_compressor_t *)base; - - memset(cfg, 0, sizeof(*cfg)); - cfg->id = SQFS_COMP_LZO; - cfg->block_size = lzo->block_size; - - cfg->opt.lzo.algorithm = lzo->algorithm; - cfg->level = lzo->level; - - if (base->do_block == lzo_uncomp_block) - cfg->flags |= SQFS_COMP_FLAG_UNCOMPRESS; -} - -static sqfs_object_t *lzo_create_copy(const sqfs_object_t *cmp) -{ - const lzo_compressor_t *other = (const lzo_compressor_t *)cmp; - lzo_compressor_t *lzo; - - lzo = calloc(1, sizeof(*lzo) + other->buf_size); - if (lzo == NULL) - return NULL; - - memcpy(lzo, other, sizeof(*lzo)); - return (sqfs_object_t *)lzo; -} - -static void lzo_destroy(sqfs_object_t *base) -{ - free(base); -} - -int lzo_compressor_create(const sqfs_compressor_config_t *cfg, - sqfs_compressor_t **out) -{ - sqfs_compressor_t *base; - lzo_compressor_t *lzo; - size_t scratch_size; - - if (cfg->flags & ~SQFS_COMP_FLAG_GENERIC_ALL) - return SQFS_ERROR_UNSUPPORTED; - - if (cfg->opt.lzo.algorithm >= LZO_NUM_ALGS || - lzo_algs[cfg->opt.lzo.algorithm].compress == NULL) { - return SQFS_ERROR_UNSUPPORTED; - } - - if (cfg->opt.lzo.algorithm == SQFS_LZO1X_999) { - if (cfg->level > SQFS_LZO_MAX_LEVEL) - return SQFS_ERROR_UNSUPPORTED; - } else if (cfg->level != 0) { - return SQFS_ERROR_UNSUPPORTED; - } - - /* XXX: liblzo does not do bounds checking internally, - we need our own internal scratch buffer at worst case size... */ - if (cfg->flags & SQFS_COMP_FLAG_UNCOMPRESS) { - scratch_size = 0; - } else { - scratch_size = cfg->block_size; - if (scratch_size < SQFS_META_BLOCK_SIZE) - scratch_size = SQFS_META_BLOCK_SIZE; - - scratch_size = LZO_MAX_SIZE(scratch_size); - } - - /* ...in addition to the LZO work space buffer of course */ - scratch_size += lzo_algs[cfg->opt.lzo.algorithm].bufsize; - - lzo = calloc(1, sizeof(*lzo) + scratch_size); - base = (sqfs_compressor_t *)lzo; - - if (lzo == NULL) - return SQFS_ERROR_ALLOC; - - sqfs_object_init(lzo, lzo_destroy, lzo_create_copy); - - lzo->block_size = cfg->block_size; - lzo->algorithm = cfg->opt.lzo.algorithm; - lzo->level = cfg->level; - lzo->buf_size = scratch_size; - lzo->work_size = lzo_algs[cfg->opt.lzo.algorithm].bufsize; - - base->get_configuration = lzo_get_configuration; - base->do_block = (cfg->flags & SQFS_COMP_FLAG_UNCOMPRESS) ? - lzo_uncomp_block : lzo_comp_block; - base->write_options = lzo_write_options; - base->read_options = lzo_read_options; - - *out = base; - return 0; -} diff --git a/lib/common/comp_opt.c b/lib/common/comp_opt.c deleted file mode 100644 index d605c0c..0000000 --- a/lib/common/comp_opt.c +++ /dev/null @@ -1,378 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * comp_opt.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "config.h" -#include "common.h" - -#include -#include -#include -#include -#include - -typedef struct { - const char *name; - sqfs_u16 flag; -} flag_t; - -static const flag_t gzip_flags[] = { - { "default", SQFS_COMP_FLAG_GZIP_DEFAULT }, - { "filtered", SQFS_COMP_FLAG_GZIP_FILTERED }, - { "huffman", SQFS_COMP_FLAG_GZIP_HUFFMAN }, - { "rle", SQFS_COMP_FLAG_GZIP_RLE }, - { "fixed", SQFS_COMP_FLAG_GZIP_FIXED }, -}; - -static const flag_t xz_flags[] = { - { "x86", SQFS_COMP_FLAG_XZ_X86 }, - { "powerpc", SQFS_COMP_FLAG_XZ_POWERPC }, - { "ia64", SQFS_COMP_FLAG_XZ_IA64 }, - { "arm", SQFS_COMP_FLAG_XZ_ARM }, - { "armthumb", SQFS_COMP_FLAG_XZ_ARMTHUMB }, - { "sparc", SQFS_COMP_FLAG_XZ_SPARC }, - { "extreme", SQFS_COMP_FLAG_XZ_EXTREME }, -}; - -static const flag_t lzma_flags[] = { - { "extreme", SQFS_COMP_FLAG_LZMA_EXTREME }, -}; - -static const flag_t lz4_flags[] = { - { "hc", SQFS_COMP_FLAG_LZ4_HC }, -}; - -static const struct { - const flag_t *flags; - size_t count; -} comp_flags[SQFS_COMP_MAX + 1] = { - [SQFS_COMP_GZIP] = { gzip_flags, sizeof(gzip_flags) / sizeof(flag_t) }, - [SQFS_COMP_XZ] = { xz_flags, sizeof(xz_flags) / sizeof(flag_t) }, - [SQFS_COMP_LZMA] = { lzma_flags, sizeof(lzma_flags) / sizeof(flag_t) }, - [SQFS_COMP_LZ4] = { lz4_flags, sizeof(lz4_flags) / sizeof(flag_t) }, -}; - -static const char *lzo_algs[] = { - [SQFS_LZO1X_1] = "lzo1x_1", - [SQFS_LZO1X_1_11] = "lzo1x_1_11", - [SQFS_LZO1X_1_12] = "lzo1x_1_12", - [SQFS_LZO1X_1_15] = "lzo1x_1_15", - [SQFS_LZO1X_999] = "lzo1x_999", -}; - -static int set_flag(sqfs_compressor_config_t *cfg, const char *name) -{ - const flag_t *flags = comp_flags[cfg->id].flags; - size_t i, num_flags = comp_flags[cfg->id].count; - - for (i = 0; i < num_flags; ++i) { - if (strcmp(flags[i].name, name) == 0) { - cfg->flags |= flags[i].flag; - return 0; - } - } - - return -1; -} - -static int find_lzo_alg(sqfs_compressor_config_t *cfg, const char *name) -{ - size_t i; - - for (i = 0; i < sizeof(lzo_algs) / sizeof(lzo_algs[0]); ++i) { - if (strcmp(lzo_algs[i], name) == 0) { - cfg->opt.lzo.algorithm = i; - return 0; - } - } - - return -1; -} - -enum { - OPT_WINDOW = 0, - OPT_LEVEL, - OPT_ALG, - OPT_DICT, - OPT_LC, - OPT_LP, - OPT_PB, - OPT_COUNT, -}; -static char *const token[] = { - [OPT_WINDOW] = (char *)"window", - [OPT_LEVEL] = (char *)"level", - [OPT_ALG] = (char *)"algorithm", - [OPT_DICT] = (char *)"dictsize", - [OPT_LC] = (char *)"lc", - [OPT_LP] = (char *)"lp", - [OPT_PB] = (char *)"pb", - NULL -}; - -static int opt_available[SQFS_COMP_MAX + 1] = { - [SQFS_COMP_GZIP] = (1 << OPT_WINDOW) | (1 << OPT_LEVEL), - [SQFS_COMP_XZ] = (1 << OPT_LEVEL) | (1 << OPT_DICT) | (1 << OPT_LC) | - (1 << OPT_LP) | (1 << OPT_PB), - [SQFS_COMP_LZMA] = (1 << OPT_LEVEL) | (1 << OPT_DICT) | (1 << OPT_LC) | - (1 << OPT_LP) | (1 << OPT_PB), - [SQFS_COMP_ZSTD] = (1 << OPT_LEVEL), - [SQFS_COMP_LZO] = (1 << OPT_LEVEL) | (1 << OPT_ALG), -}; - -static const struct { - int min; - int max; -} value_range[SQFS_COMP_MAX + 1][OPT_COUNT] = { - [SQFS_COMP_GZIP] = { - [OPT_LEVEL] = { SQFS_GZIP_MIN_LEVEL, SQFS_GZIP_MAX_LEVEL }, - [OPT_WINDOW] = { SQFS_GZIP_MIN_WINDOW, SQFS_GZIP_MAX_WINDOW }, - }, - [SQFS_COMP_XZ] = { - [OPT_LEVEL] = { SQFS_XZ_MIN_LEVEL, SQFS_XZ_MAX_LEVEL }, - [OPT_DICT] = { SQFS_XZ_MIN_DICT_SIZE, SQFS_XZ_MAX_DICT_SIZE }, - [OPT_LC] = { SQFS_XZ_MIN_LC, SQFS_XZ_MAX_LC }, - [OPT_LP] = { SQFS_XZ_MIN_LP, SQFS_XZ_MAX_LP }, - [OPT_PB] = { SQFS_XZ_MIN_PB, SQFS_XZ_MAX_PB }, - }, - [SQFS_COMP_LZMA] = { - [OPT_LEVEL] = { SQFS_LZMA_MIN_LEVEL, SQFS_LZMA_MAX_LEVEL }, - [OPT_DICT] = { SQFS_LZMA_MIN_DICT_SIZE, - SQFS_LZMA_MAX_DICT_SIZE }, - [OPT_LC] = { SQFS_LZMA_MIN_LC, SQFS_LZMA_MAX_LC }, - [OPT_LP] = { SQFS_LZMA_MIN_LP, SQFS_LZMA_MAX_LP }, - [OPT_PB] = { SQFS_LZMA_MIN_PB, SQFS_LZMA_MAX_PB }, - }, - [SQFS_COMP_ZSTD] = { - [OPT_LEVEL] = { SQFS_ZSTD_MIN_LEVEL, SQFS_ZSTD_MAX_LEVEL }, - }, - [SQFS_COMP_LZO] = { - [OPT_LEVEL] = { SQFS_LZO_MIN_LEVEL, SQFS_LZO_MAX_LEVEL }, - }, -}; - -int compressor_cfg_init_options(sqfs_compressor_config_t *cfg, - SQFS_COMPRESSOR id, - size_t block_size, char *options) -{ - char *subopts, *value; - int opt, ival; - size_t szval; - - if (sqfs_compressor_config_init(cfg, id, block_size, 0)) - return -1; - - if (options == NULL) - return 0; - - subopts = options; - - while (*subopts != '\0') { - opt = getsubopt(&subopts, token, &value); - - if (opt < 0) { - if (set_flag(cfg, value)) - goto fail_opt; - continue; - } - - if (!(opt_available[cfg->id] & (1 << opt))) - goto fail_opt; - - if (value == NULL) - goto fail_value; - - if (opt == OPT_ALG) { - if (find_lzo_alg(cfg, value)) - goto fail_lzo_alg; - continue; - } - - if (opt == OPT_DICT) { - if (parse_size("Parsing LZMA dictionary size", - &szval, value, cfg->block_size)) { - return -1; - } - ival = szval; - } else { - ival = strtol(value, NULL, 10); - } - - if (ival < value_range[cfg->id][opt].min) - goto fail_range; - if (ival > value_range[cfg->id][opt].max) - goto fail_range; - - switch (opt) { - case OPT_LEVEL: cfg->level = ival; break; - case OPT_LC: cfg->opt.xz.lc = ival; break; - case OPT_LP: cfg->opt.xz.lp = ival; break; - case OPT_PB: cfg->opt.xz.pb = ival; break; - case OPT_WINDOW: cfg->opt.gzip.window_size = ival; break; - case OPT_DICT: cfg->opt.xz.dict_size = ival; break; - default: - break; - } - } - - if (cfg->id == SQFS_COMP_XZ || cfg->id == SQFS_COMP_LZMA) { - if ((cfg->opt.xz.lp + cfg->opt.xz.lc) > 4) - goto fail_sum_lp_lc; - } - - return 0; -fail_sum_lp_lc: - fputs("Sum of XZ lc + lp must not exceed 4.\n", stderr); - return -1; -fail_lzo_alg: - fprintf(stderr, "Unknown lzo variant '%s'.\n", value); - return -1; -fail_range: - fprintf(stderr, "`%s` must be a number between %d and %d.\n", - token[opt], value_range[cfg->id][opt].min, - value_range[cfg->id][opt].max); - return -1; -fail_opt: - fprintf(stderr, "Unknown compressor option '%s'.\n", value); - return -1; -fail_value: - fprintf(stderr, "Missing value for compressor option '%s'.\n", - token[opt]); - return -1; -} - -typedef void (*compressor_help_fun_t)(void); - -static void gzip_print_help(void) -{ - size_t i; - - printf( -"Available options for gzip compressor:\n" -"\n" -" level= Compression level. Value from 1 to 9.\n" -" Defaults to %d.\n" -" window= Deflate compression window size. Value from 8 to 15.\n" -" Defaults to %d.\n" -"\n" -"In additon to the options, one or more strategies can be specified.\n" -"If multiple stratgies are provided, the one yielding the best compression\n" -"ratio will be used.\n" -"\n" -"The following strategies are available:\n", - SQFS_GZIP_DEFAULT_LEVEL, SQFS_GZIP_DEFAULT_WINDOW); - - for (i = 0; i < sizeof(gzip_flags) / sizeof(gzip_flags[0]); ++i) - printf("\t%s\n", gzip_flags[i].name); -} - -static void lz4_print_help(void) -{ - fputs("Available options for lz4 compressor:\n" - "\n" - " hc If present, use slower but better compressing\n" - " variant of lz4.\n" - "\n", - stdout); -} - -static void lzo_print_help(void) -{ - size_t i; - - fputs("Available options for lzo compressor:\n" - "\n" - " algorithm= Specify the variant of lzo to use.\n" - " Defaults to 'lzo1x_999'.\n" - " level= For lzo1x_999, the compression level.\n" - " Value from 1 to 9. Defaults to 8.\n" - " Ignored if algorithm is not lzo1x_999.\n" - "\n" - "Available algorithms:\n", - stdout); - - for (i = 0; i < sizeof(lzo_algs) / sizeof(lzo_algs[0]); ++i) - printf("\t%s\n", lzo_algs[i]); -} - -static void xz_lzma_print_help(void) -{ - size_t i; - - printf( -"Available options for LZMA and XZ (LZMA v2) compressors:\n" -"\n" -" dictsize= Dictionary size. Either a value in bytes or a\n" -" percentage of the block size. Defaults to 100%%.\n" -" The suffix '%%' indicates a percentage. 'K' and 'M'\n" -" can also be used for kibi and mebi bytes\n" -" respecitively.\n" -" level= Compression level. Value from %d to %d.\n" -" For XZ, defaults to %d, for LZMA defaults to %d.\n" -" lc= Number of literal context bits.\n" -" How many of the highest bits of the previous\n" -" uncompressed byte to take into account when\n" -" predicting the bits of the next byte.\n" -" Default is %d.\n" -" lp= Number of literal position bits.\n" -" Affects what kind of alignment in the uncompressed\n" -" data is assumed when encoding bytes.\n" -" Default is %d.\n" -" pb= Number of position bits.\n" -" This is the log2 of the assumed underlying alignment\n" -" of the input data, i.e. pb=0 means single byte\n" -" allignment, pb=1 means 16 bit, 2 means 32 bit.\n" -" Default is %d.\n" -" extreme If this flag is set, try to crunch the data extra hard\n" -" without increasing the decompressors memory\n" -" requirements." -"\n" -"If values are set, the sum of lc + lp must not exceed 4.\n" -"The maximum for pb is %d.\n" -"\n" -"In additon to the options, for the XZ compressor, one or more bcj filters\n" -"can be specified.\n" -"If multiple filters are provided, the one yielding the best compression\n" -"ratio will be used.\n" -"\n" -"The following filters are available:\n", - SQFS_XZ_MIN_LEVEL, SQFS_XZ_MAX_LEVEL, - SQFS_XZ_DEFAULT_LEVEL, SQFS_LZMA_DEFAULT_LEVEL, - SQFS_XZ_DEFAULT_LC, SQFS_XZ_DEFAULT_LP, SQFS_XZ_DEFAULT_PB, - SQFS_XZ_MAX_PB); - - for (i = 0; i < sizeof(xz_flags) / sizeof(xz_flags[0]); ++i) - printf("\t%s\n", xz_flags[i].name); -} - -static void zstd_print_help(void) -{ - printf("Available options for zstd compressor:\n" - "\n" - " level= Set compression level. Defaults to %d.\n" - " Maximum is %d.\n" - "\n", - SQFS_ZSTD_DEFAULT_LEVEL, SQFS_ZSTD_MAX_LEVEL); -} - -static const compressor_help_fun_t helpfuns[SQFS_COMP_MAX + 1] = { - [SQFS_COMP_GZIP] = gzip_print_help, - [SQFS_COMP_XZ] = xz_lzma_print_help, - [SQFS_COMP_LZMA] = xz_lzma_print_help, - [SQFS_COMP_LZO] = lzo_print_help, - [SQFS_COMP_LZ4] = lz4_print_help, - [SQFS_COMP_ZSTD] = zstd_print_help, -}; - -void compressor_print_help(SQFS_COMPRESSOR id) -{ - if (id < SQFS_COMP_MIN || id > SQFS_COMP_MAX) - return; - - if (helpfuns[id] == NULL) - return; - - helpfuns[id](); -} diff --git a/lib/common/compress.c b/lib/common/compress.c deleted file mode 100644 index 1e0ca06..0000000 --- a/lib/common/compress.c +++ /dev/null @@ -1,88 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * compress.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "config.h" -#include "compress_cli.h" - -#include -#include - -static int cmp_ids[] = { - SQFS_COMP_XZ, - SQFS_COMP_ZSTD, - SQFS_COMP_GZIP, - SQFS_COMP_LZ4, - SQFS_COMP_LZO, -}; - -SQFS_COMPRESSOR compressor_get_default(void) -{ - sqfs_compressor_config_t cfg; - sqfs_compressor_t *temp; - size_t i; - int ret; - - for (i = 0; i < sizeof(cmp_ids) / sizeof(cmp_ids[0]); ++i) { - sqfs_compressor_config_init(&cfg, cmp_ids[i], - SQFS_DEFAULT_BLOCK_SIZE, 0); - - ret = sqfs_compressor_create(&cfg, &temp); - - if (ret == 0) { - sqfs_drop(temp); - return cmp_ids[i]; - } - } - -#ifdef WITH_LZO - return SQFS_COMP_LZO; -#else - assert(0); -#endif -} - -void compressor_print_available(void) -{ - sqfs_compressor_config_t cfg; - sqfs_compressor_t *temp; - bool have_compressor; - int i, ret, defcomp; - const char *name; - - defcomp = compressor_get_default(); - - fputs("Available SquashFS block compressors:\n", stdout); - - for (i = SQFS_COMP_MIN; i <= SQFS_COMP_MAX; ++i) { - sqfs_compressor_config_init(&cfg, i, - SQFS_DEFAULT_BLOCK_SIZE, 0); - - ret = sqfs_compressor_create(&cfg, &temp); - have_compressor = false; - - if (ret == 0) { - sqfs_drop(temp); - have_compressor = true; - } else { -#ifdef WITH_LZO - if (i == SQFS_COMP_LZO) - have_compressor = true; -#endif - } - - if (have_compressor) { - name = sqfs_compressor_name_from_id(i); - - if (defcomp == i) { - printf("\t%s (default)\n", name); - } else { - printf("\t%s\n", name); - } - } - } - - fputc('\n', stdout); -} diff --git a/lib/common/data_reader_dump.c b/lib/common/data_reader_dump.c deleted file mode 100644 index 7902c25..0000000 --- a/lib/common/data_reader_dump.c +++ /dev/null @@ -1,65 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * data_reader_dump.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "common.h" - -#include -#include -#include -#include - -int sqfs_data_reader_dump(const char *name, sqfs_data_reader_t *data, - const sqfs_inode_generic_t *inode, - ostream_t *fp, size_t block_size) -{ - size_t i, diff, chunk_size; - sqfs_u64 filesz; - sqfs_u8 *chunk; - int err; - - sqfs_inode_get_file_size(inode, &filesz); - - for (i = 0; i < sqfs_inode_get_file_block_count(inode); ++i) { - diff = (filesz < block_size) ? filesz : block_size; - - if (SQFS_IS_SPARSE_BLOCK(inode->extra[i])) { - if (ostream_append_sparse(fp, diff)) - return -1; - } else { - err = sqfs_data_reader_get_block(data, inode, i, - &chunk_size, &chunk); - if (err) { - sqfs_perror(name, "reading data block", err); - return -1; - } - - err = ostream_append(fp, chunk, chunk_size); - free(chunk); - - if (err) - return -1; - } - - filesz -= diff; - } - - if (filesz > 0) { - err = sqfs_data_reader_get_fragment(data, inode, - &chunk_size, &chunk); - if (err) { - sqfs_perror(name, "reading fragment block", err); - return -1; - } - - err = ostream_append(fp, chunk, chunk_size); - free(chunk); - - if (err) - return -1; - } - - return 0; -} diff --git a/lib/common/data_writer.c b/lib/common/data_writer.c deleted file mode 100644 index ceccaac..0000000 --- a/lib/common/data_writer.c +++ /dev/null @@ -1,54 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * data_writer.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "common.h" - -static sqfs_u8 buffer[4096]; - -int write_data_from_file(const char *filename, sqfs_block_processor_t *data, - sqfs_inode_generic_t **inode, sqfs_file_t *file, - int flags) -{ - sqfs_u64 filesz, offset; - size_t diff; - int ret; - - ret = sqfs_block_processor_begin_file(data, inode, NULL, flags); - if (ret) { - sqfs_perror(filename, "beginning file data blocks", ret); - return -1; - } - - filesz = file->get_size(file); - - for (offset = 0; offset < filesz; offset += diff) { - if (filesz - offset > sizeof(buffer)) { - diff = sizeof(buffer); - } else { - diff = filesz - offset; - } - - ret = file->read_at(file, offset, buffer, diff); - if (ret) { - sqfs_perror(filename, "reading file range", ret); - return -1; - } - - ret = sqfs_block_processor_append(data, buffer, diff); - if (ret) { - sqfs_perror(filename, "packing file data", ret); - return -1; - } - } - - ret = sqfs_block_processor_end_file(data); - if (ret) { - sqfs_perror(filename, "finishing file data", ret); - return -1; - } - - return 0; -} diff --git a/lib/common/data_writer_ostream.c b/lib/common/data_writer_ostream.c deleted file mode 100644 index fbd0431..0000000 --- a/lib/common/data_writer_ostream.c +++ /dev/null @@ -1,91 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * data_writer_ostream.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "config.h" -#include "common.h" - -#include - -typedef struct{ - ostream_t base; - - sqfs_block_processor_t *proc; - const char *filename; -} data_writer_ostream_t; - -static int stream_append(ostream_t *base, const void *data, size_t size) -{ - data_writer_ostream_t *strm = (data_writer_ostream_t *)base; - int ret; - - ret = sqfs_block_processor_append(strm->proc, data, size); - - if (ret != 0) { - sqfs_perror(strm->filename, NULL, ret); - return -1; - } - - return 0; -} - -static int stream_flush(ostream_t *base) -{ - data_writer_ostream_t *strm = (data_writer_ostream_t *)base; - int ret; - - ret = sqfs_block_processor_end_file(strm->proc); - - if (ret != 0) { - sqfs_perror(strm->filename, NULL, ret); - return -1; - } - - return 0; -} - -static const char *stream_get_filename(ostream_t *base) -{ - data_writer_ostream_t *strm = (data_writer_ostream_t *)base; - - return strm->filename; -} - -static void stream_destroy(sqfs_object_t *base) -{ - free(base); -} - -ostream_t *data_writer_ostream_create(const char *filename, - sqfs_block_processor_t *proc, - sqfs_inode_generic_t **inode, - int flags) -{ - data_writer_ostream_t *strm = calloc(1, sizeof(*strm)); - ostream_t *base = (ostream_t *)strm; - int ret; - - if (strm == NULL) { - perror(filename); - return NULL; - } - - sqfs_object_init(strm, stream_destroy, NULL); - - ret = sqfs_block_processor_begin_file(proc, inode, NULL, flags); - - if (ret != 0) { - sqfs_perror(filename, NULL, ret); - free(strm); - return NULL; - } - - strm->proc = proc; - strm->filename = filename; - base->append = stream_append; - base->flush = stream_flush; - base->get_filename = stream_get_filename; - return base; -} diff --git a/lib/common/hardlink.c b/lib/common/hardlink.c deleted file mode 100644 index e43df33..0000000 --- a/lib/common/hardlink.c +++ /dev/null @@ -1,101 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * hardlink.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "common.h" -#include "util/rbtree.h" -#include "util/util.h" - -#include -#include -#include - -static int map_nodes(rbtree_t *inumtree, sqfs_hard_link_t **out, - const sqfs_tree_node_t *n) -{ - const sqfs_tree_node_t *target; - sqfs_hard_link_t *lnk; - rbtree_node_t *tn; - sqfs_u32 idx; - int ret; - - /* XXX: refuse to generate hard links to directories */ - if (n->children != NULL) { - for (n = n->children; n != NULL; n = n->next) { - ret = map_nodes(inumtree, out, n); - if (ret != 0) - return ret; - } - return 0; - } - - if (!is_filename_sane((const char *)n->name, false)) - return SQFS_ERROR_CORRUPTED; - - idx = n->inode->base.inode_number; - tn = rbtree_lookup(inumtree, &idx); - - if (tn == NULL) - return rbtree_insert(inumtree, &idx, &n); - - target = *((const sqfs_tree_node_t **)rbtree_node_value(tn)); - - lnk = calloc(1, sizeof(*lnk)); - if (lnk == NULL) - return SQFS_ERROR_ALLOC; - - lnk->inode_number = idx; - ret = sqfs_tree_node_get_path(target, &lnk->target); - if (ret != 0) { - free(lnk); - return ret; - } - - if (canonicalize_name(lnk->target) != 0) { - sqfs_free(lnk->target); - free(lnk); - return SQFS_ERROR_CORRUPTED; - } - - lnk->next = (*out); - (*out) = lnk; - return 0; -} - -static int compare_inum(const void *ctx, const void *lhs, const void *rhs) -{ - sqfs_u32 l = *((const sqfs_u32 *)lhs), r = *((const sqfs_u32 *)rhs); - (void)ctx; - - return l < r ? -1 : (l > r ? 1 : 0); -} - -int sqfs_tree_find_hard_links(const sqfs_tree_node_t *root, - sqfs_hard_link_t **out) -{ - sqfs_hard_link_t *lnk = NULL; - rbtree_t inumtree; - int ret; - - ret = rbtree_init(&inumtree, sizeof(sqfs_u32), - sizeof(sqfs_tree_node_t *), - compare_inum); - if (ret != 0) - return ret; - - ret = map_nodes(&inumtree, out, root); - rbtree_cleanup(&inumtree); - - if (ret != 0) { - while ((*out) != NULL) { - lnk = (*out); - (*out) = lnk->next; - free(lnk->target); - free(lnk); - } - } - - return ret; -} diff --git a/lib/common/parse_size.c b/lib/common/parse_size.c deleted file mode 100644 index 3e79a19..0000000 --- a/lib/common/parse_size.c +++ /dev/null @@ -1,79 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * parse_size.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "common.h" - -#include -#include - -int parse_size(const char *what, size_t *out, const char *str, - size_t reference) -{ - const char *in = str; - size_t acc = 0, x; - - if (!isdigit(*in)) - goto fail_nan; - - while (isdigit(*in)) { - x = *(in++) - '0'; - - if (SZ_MUL_OV(acc, 10, &acc)) - goto fail_ov; - - if (SZ_ADD_OV(acc, x, &acc)) - goto fail_ov; - } - - switch (*in) { - case 'k': - case 'K': - if (SZ_MUL_OV(acc, 1024, &acc)) - goto fail_ov; - ++in; - break; - case 'm': - case 'M': - if (SZ_MUL_OV(acc, 1048576, &acc)) - goto fail_ov; - ++in; - break; - case 'g': - case 'G': - if (SZ_MUL_OV(acc, 1073741824, &acc)) - goto fail_ov; - ++in; - break; - case '%': - if (reference == 0) - goto fail_suffix; - - if (SZ_MUL_OV(acc, reference, &acc)) - goto fail_ov; - - acc /= 100; - break; - case '\0': - break; - default: - goto fail_suffix; - } - - if (*in != '\0') - goto fail_suffix; - - *out = acc; - return 0; -fail_nan: - fprintf(stderr, "%s: '%s' is not a number.\n", what, str); - return -1; -fail_ov: - fprintf(stderr, "%s: numeric overflow parsing '%s'.\n", what, str); - return -1; -fail_suffix: - fprintf(stderr, "%s: unknown suffix in '%s'.\n", what, str); - return -1; -} diff --git a/lib/common/perror.c b/lib/common/perror.c deleted file mode 100644 index 53a8c16..0000000 --- a/lib/common/perror.c +++ /dev/null @@ -1,79 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * print_version.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "common.h" - -#include - -void sqfs_perror(const char *file, const char *action, int error_code) -{ - const char *errstr; - - switch (error_code) { - case SQFS_ERROR_ALLOC: - errstr = "out of memory"; - break; - case SQFS_ERROR_IO: - errstr = "I/O error"; - break; - case SQFS_ERROR_COMPRESSOR: - errstr = "internal compressor error"; - break; - case SQFS_ERROR_INTERNAL: - errstr = "internal error"; - break; - case SQFS_ERROR_CORRUPTED: - errstr = "data corrupted"; - break; - case SQFS_ERROR_UNSUPPORTED: - errstr = "unknown or not supported"; - break; - case SQFS_ERROR_OVERFLOW: - errstr = "numeric overflow"; - break; - case SQFS_ERROR_OUT_OF_BOUNDS: - errstr = "location out of bounds"; - break; - case SFQS_ERROR_SUPER_MAGIC: - errstr = "wrong magic value in super block"; - break; - case SFQS_ERROR_SUPER_VERSION: - errstr = "wrong squashfs version in super block"; - break; - case SQFS_ERROR_SUPER_BLOCK_SIZE: - errstr = "invalid block size specified in super block"; - break; - case SQFS_ERROR_NOT_DIR: - errstr = "target is not a directory"; - break; - case SQFS_ERROR_NO_ENTRY: - errstr = "no such file or directory"; - break; - case SQFS_ERROR_LINK_LOOP: - errstr = "hard link loop detected"; - break; - case SQFS_ERROR_NOT_FILE: - errstr = "target is not a file"; - break; - case SQFS_ERROR_ARG_INVALID: - errstr = "invalid argument"; - break; - case SQFS_ERROR_SEQUENCE: - errstr = "illegal oder of operations"; - break; - default: - errstr = "libsquashfs returned an unknown error code"; - break; - } - - if (file != NULL) - fprintf(stderr, "%s: ", file); - - if (action != NULL) - fprintf(stderr, "%s: ", action); - - fprintf(stderr, "%s.\n", errstr); -} diff --git a/lib/common/print_size.c b/lib/common/print_size.c deleted file mode 100644 index 6e76805..0000000 --- a/lib/common/print_size.c +++ /dev/null @@ -1,38 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * print_size.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "config.h" -#include "common.h" - -void print_size(sqfs_u64 size, char *buffer, bool round_to_int) -{ - static const char *fractions = "0112334456678899"; - static const char *suffices = "kMGTPEZY"; - unsigned int fraction; - int suffix = -1; - - while (size > 1024) { - ++suffix; - fraction = size % 1024; - size /= 1024; - } - - if (suffix >= 0) { - fraction /= 64; - - if (round_to_int) { - size = fraction >= 8 ? (size + 1) : size; - - sprintf(buffer, "%u%c", (unsigned int)size, - suffices[suffix]); - } else { - sprintf(buffer, "%u.%c%c", (unsigned int)size, - fractions[fraction], suffices[suffix]); - } - } else { - sprintf(buffer, "%u", (unsigned int)size); - } -} diff --git a/lib/common/print_version.c b/lib/common/print_version.c deleted file mode 100644 index 0c7fe5c..0000000 --- a/lib/common/print_version.c +++ /dev/null @@ -1,25 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * print_version.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "common.h" - -#include - -#define LICENSE_SHORT "GPLv3+" -#define LICENSE_LONG "GNU GPL version 3 or later" -#define LICENSE_URL "https://gnu.org/licenses/gpl.html" - -static const char *version_string = -"%s (%s) %s\n" -"Copyright (c) 2019 David Oberhollenzer et al\n" -"License " LICENSE_SHORT ": " LICENSE_LONG " <" LICENSE_URL ">.\n" -"This is free software: you are free to change and redistribute it.\n" -"There is NO WARRANTY, to the extent permitted by law.\n"; - -void print_version(const char *progname) -{ - printf(version_string, progname, PACKAGE_NAME, PACKAGE_VERSION); -} diff --git a/lib/common/src/comp_lzo.c b/lib/common/src/comp_lzo.c new file mode 100644 index 0000000..2021d34 --- /dev/null +++ b/lib/common/src/comp_lzo.c @@ -0,0 +1,285 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * comp_lzo.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "config.h" +#include "compress_cli.h" +#include "compat.h" + +#include +#include +#include + +#include + +#define LZO_MAX_SIZE(size) (size + (size / 16) + 64 + 3) + +#define LZO_NUM_ALGS (sizeof(lzo_algs) / sizeof(lzo_algs[0])) + +typedef int (*lzo_cb_t)(const lzo_bytep src, lzo_uint src_len, lzo_bytep dst, + lzo_uintp dst_len, lzo_voidp wrkmem); + +static const struct { + lzo_cb_t compress; + size_t bufsize; +} lzo_algs[] = { + [SQFS_LZO1X_1] = { + .compress = lzo1x_1_compress, + .bufsize = LZO1X_1_MEM_COMPRESS, + }, + [SQFS_LZO1X_1_11] = { + .compress = lzo1x_1_11_compress, + .bufsize = LZO1X_1_11_MEM_COMPRESS, + }, + [SQFS_LZO1X_1_12] = { + .compress = lzo1x_1_12_compress, + .bufsize = LZO1X_1_12_MEM_COMPRESS, + }, + [SQFS_LZO1X_1_15] = { + .compress = lzo1x_1_15_compress, + .bufsize = LZO1X_1_15_MEM_COMPRESS, + }, + [SQFS_LZO1X_999] = { + .compress = lzo1x_999_compress, + .bufsize = LZO1X_999_MEM_COMPRESS, + }, +}; + +typedef struct { + sqfs_compressor_t base; + size_t block_size; + int algorithm; + int level; + + size_t buf_size; + size_t work_size; + + sqfs_u8 buffer[]; +} lzo_compressor_t; + +typedef struct { + sqfs_u32 algorithm; + sqfs_u32 level; +} lzo_options_t; + +static int lzo_write_options(sqfs_compressor_t *base, sqfs_file_t *file) +{ + lzo_compressor_t *lzo = (lzo_compressor_t *)base; + sqfs_u8 buffer[sizeof(lzo_options_t) + 2]; + lzo_options_t opt; + sqfs_u16 header; + int ret; + + if (lzo->algorithm == SQFS_LZO_DEFAULT_ALG && + lzo->level == SQFS_LZO_DEFAULT_LEVEL) { + return 0; + } + + opt.algorithm = htole32(lzo->algorithm); + + if (lzo->algorithm == SQFS_LZO1X_999) { + opt.level = htole32(lzo->level); + } else { + opt.level = 0; + } + + header = htole16(0x8000 | sizeof(opt)); + + memcpy(buffer, &header, sizeof(header)); + memcpy(buffer + 2, &opt, sizeof(opt)); + + ret = file->write_at(file, sizeof(sqfs_super_t), + buffer, sizeof(buffer)); + + return ret ? ret : (int)sizeof(buffer); +} + +static int lzo_read_options(sqfs_compressor_t *base, sqfs_file_t *file) +{ + lzo_compressor_t *lzo = (lzo_compressor_t *)base; + sqfs_u8 buffer[sizeof(lzo_options_t) + 2]; + lzo_options_t opt; + sqfs_u16 header; + int ret; + + ret = file->read_at(file, sizeof(sqfs_super_t), + buffer, sizeof(buffer)); + if (ret) + return ret; + + memcpy(&header, buffer, sizeof(header)); + if (le16toh(header) != (0x8000 | sizeof(opt))) + return SQFS_ERROR_CORRUPTED; + + memcpy(&opt, buffer + 2, sizeof(opt)); + lzo->algorithm = le32toh(opt.algorithm); + lzo->level = le32toh(opt.level); + + switch(lzo->algorithm) { + case SQFS_LZO1X_1: + case SQFS_LZO1X_1_11: + case SQFS_LZO1X_1_12: + case SQFS_LZO1X_1_15: + if (lzo->level != 0) + return SQFS_ERROR_UNSUPPORTED; + break; + case SQFS_LZO1X_999: + if (lzo->level < 1 || lzo->level > 9) + return SQFS_ERROR_UNSUPPORTED; + break; + default: + return SQFS_ERROR_UNSUPPORTED; + } + + return 0; +} + +static sqfs_s32 lzo_comp_block(sqfs_compressor_t *base, const sqfs_u8 *in, + sqfs_u32 size, sqfs_u8 *out, sqfs_u32 outsize) +{ + lzo_compressor_t *lzo = (lzo_compressor_t *)base; + void *scratch; + lzo_uint len; + int ret; + + if (size >= 0x7FFFFFFF) + return 0; + + scratch = lzo->buffer + lzo->work_size; + len = lzo->buf_size - lzo->work_size; + + if (lzo->algorithm == SQFS_LZO1X_999 && + lzo->level != SQFS_LZO_DEFAULT_LEVEL) { + ret = lzo1x_999_compress_level(in, size, scratch, &len, + lzo->buffer, NULL, 0, 0, + lzo->level); + } else { + ret = lzo_algs[lzo->algorithm].compress(in, size, scratch, + &len, lzo->buffer); + } + + if (ret != LZO_E_OK) + return SQFS_ERROR_COMPRESSOR; + + if (len < size && len <= outsize) { + memcpy(out, scratch, len); + return len; + } + + return 0; +} + +static sqfs_s32 lzo_uncomp_block(sqfs_compressor_t *base, const sqfs_u8 *in, + sqfs_u32 size, sqfs_u8 *out, sqfs_u32 outsize) +{ + lzo_compressor_t *lzo = (lzo_compressor_t *)base; + lzo_uint len = outsize; + int ret; + + if (outsize >= 0x7FFFFFFF) + return 0; + + ret = lzo1x_decompress_safe(in, size, out, &len, lzo->buffer); + + if (ret != LZO_E_OK) + return SQFS_ERROR_COMPRESSOR; + + return len; +} + +static void lzo_get_configuration(const sqfs_compressor_t *base, + sqfs_compressor_config_t *cfg) +{ + const lzo_compressor_t *lzo = (const lzo_compressor_t *)base; + + memset(cfg, 0, sizeof(*cfg)); + cfg->id = SQFS_COMP_LZO; + cfg->block_size = lzo->block_size; + + cfg->opt.lzo.algorithm = lzo->algorithm; + cfg->level = lzo->level; + + if (base->do_block == lzo_uncomp_block) + cfg->flags |= SQFS_COMP_FLAG_UNCOMPRESS; +} + +static sqfs_object_t *lzo_create_copy(const sqfs_object_t *cmp) +{ + const lzo_compressor_t *other = (const lzo_compressor_t *)cmp; + lzo_compressor_t *lzo; + + lzo = calloc(1, sizeof(*lzo) + other->buf_size); + if (lzo == NULL) + return NULL; + + memcpy(lzo, other, sizeof(*lzo)); + return (sqfs_object_t *)lzo; +} + +static void lzo_destroy(sqfs_object_t *base) +{ + free(base); +} + +int lzo_compressor_create(const sqfs_compressor_config_t *cfg, + sqfs_compressor_t **out) +{ + sqfs_compressor_t *base; + lzo_compressor_t *lzo; + size_t scratch_size; + + if (cfg->flags & ~SQFS_COMP_FLAG_GENERIC_ALL) + return SQFS_ERROR_UNSUPPORTED; + + if (cfg->opt.lzo.algorithm >= LZO_NUM_ALGS || + lzo_algs[cfg->opt.lzo.algorithm].compress == NULL) { + return SQFS_ERROR_UNSUPPORTED; + } + + if (cfg->opt.lzo.algorithm == SQFS_LZO1X_999) { + if (cfg->level > SQFS_LZO_MAX_LEVEL) + return SQFS_ERROR_UNSUPPORTED; + } else if (cfg->level != 0) { + return SQFS_ERROR_UNSUPPORTED; + } + + /* XXX: liblzo does not do bounds checking internally, + we need our own internal scratch buffer at worst case size... */ + if (cfg->flags & SQFS_COMP_FLAG_UNCOMPRESS) { + scratch_size = 0; + } else { + scratch_size = cfg->block_size; + if (scratch_size < SQFS_META_BLOCK_SIZE) + scratch_size = SQFS_META_BLOCK_SIZE; + + scratch_size = LZO_MAX_SIZE(scratch_size); + } + + /* ...in addition to the LZO work space buffer of course */ + scratch_size += lzo_algs[cfg->opt.lzo.algorithm].bufsize; + + lzo = calloc(1, sizeof(*lzo) + scratch_size); + base = (sqfs_compressor_t *)lzo; + + if (lzo == NULL) + return SQFS_ERROR_ALLOC; + + sqfs_object_init(lzo, lzo_destroy, lzo_create_copy); + + lzo->block_size = cfg->block_size; + lzo->algorithm = cfg->opt.lzo.algorithm; + lzo->level = cfg->level; + lzo->buf_size = scratch_size; + lzo->work_size = lzo_algs[cfg->opt.lzo.algorithm].bufsize; + + base->get_configuration = lzo_get_configuration; + base->do_block = (cfg->flags & SQFS_COMP_FLAG_UNCOMPRESS) ? + lzo_uncomp_block : lzo_comp_block; + base->write_options = lzo_write_options; + base->read_options = lzo_read_options; + + *out = base; + return 0; +} diff --git a/lib/common/src/comp_opt.c b/lib/common/src/comp_opt.c new file mode 100644 index 0000000..d605c0c --- /dev/null +++ b/lib/common/src/comp_opt.c @@ -0,0 +1,378 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * comp_opt.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "config.h" +#include "common.h" + +#include +#include +#include +#include +#include + +typedef struct { + const char *name; + sqfs_u16 flag; +} flag_t; + +static const flag_t gzip_flags[] = { + { "default", SQFS_COMP_FLAG_GZIP_DEFAULT }, + { "filtered", SQFS_COMP_FLAG_GZIP_FILTERED }, + { "huffman", SQFS_COMP_FLAG_GZIP_HUFFMAN }, + { "rle", SQFS_COMP_FLAG_GZIP_RLE }, + { "fixed", SQFS_COMP_FLAG_GZIP_FIXED }, +}; + +static const flag_t xz_flags[] = { + { "x86", SQFS_COMP_FLAG_XZ_X86 }, + { "powerpc", SQFS_COMP_FLAG_XZ_POWERPC }, + { "ia64", SQFS_COMP_FLAG_XZ_IA64 }, + { "arm", SQFS_COMP_FLAG_XZ_ARM }, + { "armthumb", SQFS_COMP_FLAG_XZ_ARMTHUMB }, + { "sparc", SQFS_COMP_FLAG_XZ_SPARC }, + { "extreme", SQFS_COMP_FLAG_XZ_EXTREME }, +}; + +static const flag_t lzma_flags[] = { + { "extreme", SQFS_COMP_FLAG_LZMA_EXTREME }, +}; + +static const flag_t lz4_flags[] = { + { "hc", SQFS_COMP_FLAG_LZ4_HC }, +}; + +static const struct { + const flag_t *flags; + size_t count; +} comp_flags[SQFS_COMP_MAX + 1] = { + [SQFS_COMP_GZIP] = { gzip_flags, sizeof(gzip_flags) / sizeof(flag_t) }, + [SQFS_COMP_XZ] = { xz_flags, sizeof(xz_flags) / sizeof(flag_t) }, + [SQFS_COMP_LZMA] = { lzma_flags, sizeof(lzma_flags) / sizeof(flag_t) }, + [SQFS_COMP_LZ4] = { lz4_flags, sizeof(lz4_flags) / sizeof(flag_t) }, +}; + +static const char *lzo_algs[] = { + [SQFS_LZO1X_1] = "lzo1x_1", + [SQFS_LZO1X_1_11] = "lzo1x_1_11", + [SQFS_LZO1X_1_12] = "lzo1x_1_12", + [SQFS_LZO1X_1_15] = "lzo1x_1_15", + [SQFS_LZO1X_999] = "lzo1x_999", +}; + +static int set_flag(sqfs_compressor_config_t *cfg, const char *name) +{ + const flag_t *flags = comp_flags[cfg->id].flags; + size_t i, num_flags = comp_flags[cfg->id].count; + + for (i = 0; i < num_flags; ++i) { + if (strcmp(flags[i].name, name) == 0) { + cfg->flags |= flags[i].flag; + return 0; + } + } + + return -1; +} + +static int find_lzo_alg(sqfs_compressor_config_t *cfg, const char *name) +{ + size_t i; + + for (i = 0; i < sizeof(lzo_algs) / sizeof(lzo_algs[0]); ++i) { + if (strcmp(lzo_algs[i], name) == 0) { + cfg->opt.lzo.algorithm = i; + return 0; + } + } + + return -1; +} + +enum { + OPT_WINDOW = 0, + OPT_LEVEL, + OPT_ALG, + OPT_DICT, + OPT_LC, + OPT_LP, + OPT_PB, + OPT_COUNT, +}; +static char *const token[] = { + [OPT_WINDOW] = (char *)"window", + [OPT_LEVEL] = (char *)"level", + [OPT_ALG] = (char *)"algorithm", + [OPT_DICT] = (char *)"dictsize", + [OPT_LC] = (char *)"lc", + [OPT_LP] = (char *)"lp", + [OPT_PB] = (char *)"pb", + NULL +}; + +static int opt_available[SQFS_COMP_MAX + 1] = { + [SQFS_COMP_GZIP] = (1 << OPT_WINDOW) | (1 << OPT_LEVEL), + [SQFS_COMP_XZ] = (1 << OPT_LEVEL) | (1 << OPT_DICT) | (1 << OPT_LC) | + (1 << OPT_LP) | (1 << OPT_PB), + [SQFS_COMP_LZMA] = (1 << OPT_LEVEL) | (1 << OPT_DICT) | (1 << OPT_LC) | + (1 << OPT_LP) | (1 << OPT_PB), + [SQFS_COMP_ZSTD] = (1 << OPT_LEVEL), + [SQFS_COMP_LZO] = (1 << OPT_LEVEL) | (1 << OPT_ALG), +}; + +static const struct { + int min; + int max; +} value_range[SQFS_COMP_MAX + 1][OPT_COUNT] = { + [SQFS_COMP_GZIP] = { + [OPT_LEVEL] = { SQFS_GZIP_MIN_LEVEL, SQFS_GZIP_MAX_LEVEL }, + [OPT_WINDOW] = { SQFS_GZIP_MIN_WINDOW, SQFS_GZIP_MAX_WINDOW }, + }, + [SQFS_COMP_XZ] = { + [OPT_LEVEL] = { SQFS_XZ_MIN_LEVEL, SQFS_XZ_MAX_LEVEL }, + [OPT_DICT] = { SQFS_XZ_MIN_DICT_SIZE, SQFS_XZ_MAX_DICT_SIZE }, + [OPT_LC] = { SQFS_XZ_MIN_LC, SQFS_XZ_MAX_LC }, + [OPT_LP] = { SQFS_XZ_MIN_LP, SQFS_XZ_MAX_LP }, + [OPT_PB] = { SQFS_XZ_MIN_PB, SQFS_XZ_MAX_PB }, + }, + [SQFS_COMP_LZMA] = { + [OPT_LEVEL] = { SQFS_LZMA_MIN_LEVEL, SQFS_LZMA_MAX_LEVEL }, + [OPT_DICT] = { SQFS_LZMA_MIN_DICT_SIZE, + SQFS_LZMA_MAX_DICT_SIZE }, + [OPT_LC] = { SQFS_LZMA_MIN_LC, SQFS_LZMA_MAX_LC }, + [OPT_LP] = { SQFS_LZMA_MIN_LP, SQFS_LZMA_MAX_LP }, + [OPT_PB] = { SQFS_LZMA_MIN_PB, SQFS_LZMA_MAX_PB }, + }, + [SQFS_COMP_ZSTD] = { + [OPT_LEVEL] = { SQFS_ZSTD_MIN_LEVEL, SQFS_ZSTD_MAX_LEVEL }, + }, + [SQFS_COMP_LZO] = { + [OPT_LEVEL] = { SQFS_LZO_MIN_LEVEL, SQFS_LZO_MAX_LEVEL }, + }, +}; + +int compressor_cfg_init_options(sqfs_compressor_config_t *cfg, + SQFS_COMPRESSOR id, + size_t block_size, char *options) +{ + char *subopts, *value; + int opt, ival; + size_t szval; + + if (sqfs_compressor_config_init(cfg, id, block_size, 0)) + return -1; + + if (options == NULL) + return 0; + + subopts = options; + + while (*subopts != '\0') { + opt = getsubopt(&subopts, token, &value); + + if (opt < 0) { + if (set_flag(cfg, value)) + goto fail_opt; + continue; + } + + if (!(opt_available[cfg->id] & (1 << opt))) + goto fail_opt; + + if (value == NULL) + goto fail_value; + + if (opt == OPT_ALG) { + if (find_lzo_alg(cfg, value)) + goto fail_lzo_alg; + continue; + } + + if (opt == OPT_DICT) { + if (parse_size("Parsing LZMA dictionary size", + &szval, value, cfg->block_size)) { + return -1; + } + ival = szval; + } else { + ival = strtol(value, NULL, 10); + } + + if (ival < value_range[cfg->id][opt].min) + goto fail_range; + if (ival > value_range[cfg->id][opt].max) + goto fail_range; + + switch (opt) { + case OPT_LEVEL: cfg->level = ival; break; + case OPT_LC: cfg->opt.xz.lc = ival; break; + case OPT_LP: cfg->opt.xz.lp = ival; break; + case OPT_PB: cfg->opt.xz.pb = ival; break; + case OPT_WINDOW: cfg->opt.gzip.window_size = ival; break; + case OPT_DICT: cfg->opt.xz.dict_size = ival; break; + default: + break; + } + } + + if (cfg->id == SQFS_COMP_XZ || cfg->id == SQFS_COMP_LZMA) { + if ((cfg->opt.xz.lp + cfg->opt.xz.lc) > 4) + goto fail_sum_lp_lc; + } + + return 0; +fail_sum_lp_lc: + fputs("Sum of XZ lc + lp must not exceed 4.\n", stderr); + return -1; +fail_lzo_alg: + fprintf(stderr, "Unknown lzo variant '%s'.\n", value); + return -1; +fail_range: + fprintf(stderr, "`%s` must be a number between %d and %d.\n", + token[opt], value_range[cfg->id][opt].min, + value_range[cfg->id][opt].max); + return -1; +fail_opt: + fprintf(stderr, "Unknown compressor option '%s'.\n", value); + return -1; +fail_value: + fprintf(stderr, "Missing value for compressor option '%s'.\n", + token[opt]); + return -1; +} + +typedef void (*compressor_help_fun_t)(void); + +static void gzip_print_help(void) +{ + size_t i; + + printf( +"Available options for gzip compressor:\n" +"\n" +" level= Compression level. Value from 1 to 9.\n" +" Defaults to %d.\n" +" window= Deflate compression window size. Value from 8 to 15.\n" +" Defaults to %d.\n" +"\n" +"In additon to the options, one or more strategies can be specified.\n" +"If multiple stratgies are provided, the one yielding the best compression\n" +"ratio will be used.\n" +"\n" +"The following strategies are available:\n", + SQFS_GZIP_DEFAULT_LEVEL, SQFS_GZIP_DEFAULT_WINDOW); + + for (i = 0; i < sizeof(gzip_flags) / sizeof(gzip_flags[0]); ++i) + printf("\t%s\n", gzip_flags[i].name); +} + +static void lz4_print_help(void) +{ + fputs("Available options for lz4 compressor:\n" + "\n" + " hc If present, use slower but better compressing\n" + " variant of lz4.\n" + "\n", + stdout); +} + +static void lzo_print_help(void) +{ + size_t i; + + fputs("Available options for lzo compressor:\n" + "\n" + " algorithm= Specify the variant of lzo to use.\n" + " Defaults to 'lzo1x_999'.\n" + " level= For lzo1x_999, the compression level.\n" + " Value from 1 to 9. Defaults to 8.\n" + " Ignored if algorithm is not lzo1x_999.\n" + "\n" + "Available algorithms:\n", + stdout); + + for (i = 0; i < sizeof(lzo_algs) / sizeof(lzo_algs[0]); ++i) + printf("\t%s\n", lzo_algs[i]); +} + +static void xz_lzma_print_help(void) +{ + size_t i; + + printf( +"Available options for LZMA and XZ (LZMA v2) compressors:\n" +"\n" +" dictsize= Dictionary size. Either a value in bytes or a\n" +" percentage of the block size. Defaults to 100%%.\n" +" The suffix '%%' indicates a percentage. 'K' and 'M'\n" +" can also be used for kibi and mebi bytes\n" +" respecitively.\n" +" level= Compression level. Value from %d to %d.\n" +" For XZ, defaults to %d, for LZMA defaults to %d.\n" +" lc= Number of literal context bits.\n" +" How many of the highest bits of the previous\n" +" uncompressed byte to take into account when\n" +" predicting the bits of the next byte.\n" +" Default is %d.\n" +" lp= Number of literal position bits.\n" +" Affects what kind of alignment in the uncompressed\n" +" data is assumed when encoding bytes.\n" +" Default is %d.\n" +" pb= Number of position bits.\n" +" This is the log2 of the assumed underlying alignment\n" +" of the input data, i.e. pb=0 means single byte\n" +" allignment, pb=1 means 16 bit, 2 means 32 bit.\n" +" Default is %d.\n" +" extreme If this flag is set, try to crunch the data extra hard\n" +" without increasing the decompressors memory\n" +" requirements." +"\n" +"If values are set, the sum of lc + lp must not exceed 4.\n" +"The maximum for pb is %d.\n" +"\n" +"In additon to the options, for the XZ compressor, one or more bcj filters\n" +"can be specified.\n" +"If multiple filters are provided, the one yielding the best compression\n" +"ratio will be used.\n" +"\n" +"The following filters are available:\n", + SQFS_XZ_MIN_LEVEL, SQFS_XZ_MAX_LEVEL, + SQFS_XZ_DEFAULT_LEVEL, SQFS_LZMA_DEFAULT_LEVEL, + SQFS_XZ_DEFAULT_LC, SQFS_XZ_DEFAULT_LP, SQFS_XZ_DEFAULT_PB, + SQFS_XZ_MAX_PB); + + for (i = 0; i < sizeof(xz_flags) / sizeof(xz_flags[0]); ++i) + printf("\t%s\n", xz_flags[i].name); +} + +static void zstd_print_help(void) +{ + printf("Available options for zstd compressor:\n" + "\n" + " level= Set compression level. Defaults to %d.\n" + " Maximum is %d.\n" + "\n", + SQFS_ZSTD_DEFAULT_LEVEL, SQFS_ZSTD_MAX_LEVEL); +} + +static const compressor_help_fun_t helpfuns[SQFS_COMP_MAX + 1] = { + [SQFS_COMP_GZIP] = gzip_print_help, + [SQFS_COMP_XZ] = xz_lzma_print_help, + [SQFS_COMP_LZMA] = xz_lzma_print_help, + [SQFS_COMP_LZO] = lzo_print_help, + [SQFS_COMP_LZ4] = lz4_print_help, + [SQFS_COMP_ZSTD] = zstd_print_help, +}; + +void compressor_print_help(SQFS_COMPRESSOR id) +{ + if (id < SQFS_COMP_MIN || id > SQFS_COMP_MAX) + return; + + if (helpfuns[id] == NULL) + return; + + helpfuns[id](); +} diff --git a/lib/common/src/compress.c b/lib/common/src/compress.c new file mode 100644 index 0000000..1e0ca06 --- /dev/null +++ b/lib/common/src/compress.c @@ -0,0 +1,88 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * compress.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "config.h" +#include "compress_cli.h" + +#include +#include + +static int cmp_ids[] = { + SQFS_COMP_XZ, + SQFS_COMP_ZSTD, + SQFS_COMP_GZIP, + SQFS_COMP_LZ4, + SQFS_COMP_LZO, +}; + +SQFS_COMPRESSOR compressor_get_default(void) +{ + sqfs_compressor_config_t cfg; + sqfs_compressor_t *temp; + size_t i; + int ret; + + for (i = 0; i < sizeof(cmp_ids) / sizeof(cmp_ids[0]); ++i) { + sqfs_compressor_config_init(&cfg, cmp_ids[i], + SQFS_DEFAULT_BLOCK_SIZE, 0); + + ret = sqfs_compressor_create(&cfg, &temp); + + if (ret == 0) { + sqfs_drop(temp); + return cmp_ids[i]; + } + } + +#ifdef WITH_LZO + return SQFS_COMP_LZO; +#else + assert(0); +#endif +} + +void compressor_print_available(void) +{ + sqfs_compressor_config_t cfg; + sqfs_compressor_t *temp; + bool have_compressor; + int i, ret, defcomp; + const char *name; + + defcomp = compressor_get_default(); + + fputs("Available SquashFS block compressors:\n", stdout); + + for (i = SQFS_COMP_MIN; i <= SQFS_COMP_MAX; ++i) { + sqfs_compressor_config_init(&cfg, i, + SQFS_DEFAULT_BLOCK_SIZE, 0); + + ret = sqfs_compressor_create(&cfg, &temp); + have_compressor = false; + + if (ret == 0) { + sqfs_drop(temp); + have_compressor = true; + } else { +#ifdef WITH_LZO + if (i == SQFS_COMP_LZO) + have_compressor = true; +#endif + } + + if (have_compressor) { + name = sqfs_compressor_name_from_id(i); + + if (defcomp == i) { + printf("\t%s (default)\n", name); + } else { + printf("\t%s\n", name); + } + } + } + + fputc('\n', stdout); +} diff --git a/lib/common/src/data_reader_dump.c b/lib/common/src/data_reader_dump.c new file mode 100644 index 0000000..7902c25 --- /dev/null +++ b/lib/common/src/data_reader_dump.c @@ -0,0 +1,65 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * data_reader_dump.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "common.h" + +#include +#include +#include +#include + +int sqfs_data_reader_dump(const char *name, sqfs_data_reader_t *data, + const sqfs_inode_generic_t *inode, + ostream_t *fp, size_t block_size) +{ + size_t i, diff, chunk_size; + sqfs_u64 filesz; + sqfs_u8 *chunk; + int err; + + sqfs_inode_get_file_size(inode, &filesz); + + for (i = 0; i < sqfs_inode_get_file_block_count(inode); ++i) { + diff = (filesz < block_size) ? filesz : block_size; + + if (SQFS_IS_SPARSE_BLOCK(inode->extra[i])) { + if (ostream_append_sparse(fp, diff)) + return -1; + } else { + err = sqfs_data_reader_get_block(data, inode, i, + &chunk_size, &chunk); + if (err) { + sqfs_perror(name, "reading data block", err); + return -1; + } + + err = ostream_append(fp, chunk, chunk_size); + free(chunk); + + if (err) + return -1; + } + + filesz -= diff; + } + + if (filesz > 0) { + err = sqfs_data_reader_get_fragment(data, inode, + &chunk_size, &chunk); + if (err) { + sqfs_perror(name, "reading fragment block", err); + return -1; + } + + err = ostream_append(fp, chunk, chunk_size); + free(chunk); + + if (err) + return -1; + } + + return 0; +} diff --git a/lib/common/src/data_writer.c b/lib/common/src/data_writer.c new file mode 100644 index 0000000..ceccaac --- /dev/null +++ b/lib/common/src/data_writer.c @@ -0,0 +1,54 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * data_writer.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "common.h" + +static sqfs_u8 buffer[4096]; + +int write_data_from_file(const char *filename, sqfs_block_processor_t *data, + sqfs_inode_generic_t **inode, sqfs_file_t *file, + int flags) +{ + sqfs_u64 filesz, offset; + size_t diff; + int ret; + + ret = sqfs_block_processor_begin_file(data, inode, NULL, flags); + if (ret) { + sqfs_perror(filename, "beginning file data blocks", ret); + return -1; + } + + filesz = file->get_size(file); + + for (offset = 0; offset < filesz; offset += diff) { + if (filesz - offset > sizeof(buffer)) { + diff = sizeof(buffer); + } else { + diff = filesz - offset; + } + + ret = file->read_at(file, offset, buffer, diff); + if (ret) { + sqfs_perror(filename, "reading file range", ret); + return -1; + } + + ret = sqfs_block_processor_append(data, buffer, diff); + if (ret) { + sqfs_perror(filename, "packing file data", ret); + return -1; + } + } + + ret = sqfs_block_processor_end_file(data); + if (ret) { + sqfs_perror(filename, "finishing file data", ret); + return -1; + } + + return 0; +} diff --git a/lib/common/src/data_writer_ostream.c b/lib/common/src/data_writer_ostream.c new file mode 100644 index 0000000..fbd0431 --- /dev/null +++ b/lib/common/src/data_writer_ostream.c @@ -0,0 +1,91 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * data_writer_ostream.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "config.h" +#include "common.h" + +#include + +typedef struct{ + ostream_t base; + + sqfs_block_processor_t *proc; + const char *filename; +} data_writer_ostream_t; + +static int stream_append(ostream_t *base, const void *data, size_t size) +{ + data_writer_ostream_t *strm = (data_writer_ostream_t *)base; + int ret; + + ret = sqfs_block_processor_append(strm->proc, data, size); + + if (ret != 0) { + sqfs_perror(strm->filename, NULL, ret); + return -1; + } + + return 0; +} + +static int stream_flush(ostream_t *base) +{ + data_writer_ostream_t *strm = (data_writer_ostream_t *)base; + int ret; + + ret = sqfs_block_processor_end_file(strm->proc); + + if (ret != 0) { + sqfs_perror(strm->filename, NULL, ret); + return -1; + } + + return 0; +} + +static const char *stream_get_filename(ostream_t *base) +{ + data_writer_ostream_t *strm = (data_writer_ostream_t *)base; + + return strm->filename; +} + +static void stream_destroy(sqfs_object_t *base) +{ + free(base); +} + +ostream_t *data_writer_ostream_create(const char *filename, + sqfs_block_processor_t *proc, + sqfs_inode_generic_t **inode, + int flags) +{ + data_writer_ostream_t *strm = calloc(1, sizeof(*strm)); + ostream_t *base = (ostream_t *)strm; + int ret; + + if (strm == NULL) { + perror(filename); + return NULL; + } + + sqfs_object_init(strm, stream_destroy, NULL); + + ret = sqfs_block_processor_begin_file(proc, inode, NULL, flags); + + if (ret != 0) { + sqfs_perror(filename, NULL, ret); + free(strm); + return NULL; + } + + strm->proc = proc; + strm->filename = filename; + base->append = stream_append; + base->flush = stream_flush; + base->get_filename = stream_get_filename; + return base; +} diff --git a/lib/common/src/hardlink.c b/lib/common/src/hardlink.c new file mode 100644 index 0000000..e43df33 --- /dev/null +++ b/lib/common/src/hardlink.c @@ -0,0 +1,101 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * hardlink.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "common.h" +#include "util/rbtree.h" +#include "util/util.h" + +#include +#include +#include + +static int map_nodes(rbtree_t *inumtree, sqfs_hard_link_t **out, + const sqfs_tree_node_t *n) +{ + const sqfs_tree_node_t *target; + sqfs_hard_link_t *lnk; + rbtree_node_t *tn; + sqfs_u32 idx; + int ret; + + /* XXX: refuse to generate hard links to directories */ + if (n->children != NULL) { + for (n = n->children; n != NULL; n = n->next) { + ret = map_nodes(inumtree, out, n); + if (ret != 0) + return ret; + } + return 0; + } + + if (!is_filename_sane((const char *)n->name, false)) + return SQFS_ERROR_CORRUPTED; + + idx = n->inode->base.inode_number; + tn = rbtree_lookup(inumtree, &idx); + + if (tn == NULL) + return rbtree_insert(inumtree, &idx, &n); + + target = *((const sqfs_tree_node_t **)rbtree_node_value(tn)); + + lnk = calloc(1, sizeof(*lnk)); + if (lnk == NULL) + return SQFS_ERROR_ALLOC; + + lnk->inode_number = idx; + ret = sqfs_tree_node_get_path(target, &lnk->target); + if (ret != 0) { + free(lnk); + return ret; + } + + if (canonicalize_name(lnk->target) != 0) { + sqfs_free(lnk->target); + free(lnk); + return SQFS_ERROR_CORRUPTED; + } + + lnk->next = (*out); + (*out) = lnk; + return 0; +} + +static int compare_inum(const void *ctx, const void *lhs, const void *rhs) +{ + sqfs_u32 l = *((const sqfs_u32 *)lhs), r = *((const sqfs_u32 *)rhs); + (void)ctx; + + return l < r ? -1 : (l > r ? 1 : 0); +} + +int sqfs_tree_find_hard_links(const sqfs_tree_node_t *root, + sqfs_hard_link_t **out) +{ + sqfs_hard_link_t *lnk = NULL; + rbtree_t inumtree; + int ret; + + ret = rbtree_init(&inumtree, sizeof(sqfs_u32), + sizeof(sqfs_tree_node_t *), + compare_inum); + if (ret != 0) + return ret; + + ret = map_nodes(&inumtree, out, root); + rbtree_cleanup(&inumtree); + + if (ret != 0) { + while ((*out) != NULL) { + lnk = (*out); + (*out) = lnk->next; + free(lnk->target); + free(lnk); + } + } + + return ret; +} diff --git a/lib/common/src/parse_size.c b/lib/common/src/parse_size.c new file mode 100644 index 0000000..3e79a19 --- /dev/null +++ b/lib/common/src/parse_size.c @@ -0,0 +1,79 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * parse_size.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "common.h" + +#include +#include + +int parse_size(const char *what, size_t *out, const char *str, + size_t reference) +{ + const char *in = str; + size_t acc = 0, x; + + if (!isdigit(*in)) + goto fail_nan; + + while (isdigit(*in)) { + x = *(in++) - '0'; + + if (SZ_MUL_OV(acc, 10, &acc)) + goto fail_ov; + + if (SZ_ADD_OV(acc, x, &acc)) + goto fail_ov; + } + + switch (*in) { + case 'k': + case 'K': + if (SZ_MUL_OV(acc, 1024, &acc)) + goto fail_ov; + ++in; + break; + case 'm': + case 'M': + if (SZ_MUL_OV(acc, 1048576, &acc)) + goto fail_ov; + ++in; + break; + case 'g': + case 'G': + if (SZ_MUL_OV(acc, 1073741824, &acc)) + goto fail_ov; + ++in; + break; + case '%': + if (reference == 0) + goto fail_suffix; + + if (SZ_MUL_OV(acc, reference, &acc)) + goto fail_ov; + + acc /= 100; + break; + case '\0': + break; + default: + goto fail_suffix; + } + + if (*in != '\0') + goto fail_suffix; + + *out = acc; + return 0; +fail_nan: + fprintf(stderr, "%s: '%s' is not a number.\n", what, str); + return -1; +fail_ov: + fprintf(stderr, "%s: numeric overflow parsing '%s'.\n", what, str); + return -1; +fail_suffix: + fprintf(stderr, "%s: unknown suffix in '%s'.\n", what, str); + return -1; +} diff --git a/lib/common/src/perror.c b/lib/common/src/perror.c new file mode 100644 index 0000000..53a8c16 --- /dev/null +++ b/lib/common/src/perror.c @@ -0,0 +1,79 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * print_version.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "common.h" + +#include + +void sqfs_perror(const char *file, const char *action, int error_code) +{ + const char *errstr; + + switch (error_code) { + case SQFS_ERROR_ALLOC: + errstr = "out of memory"; + break; + case SQFS_ERROR_IO: + errstr = "I/O error"; + break; + case SQFS_ERROR_COMPRESSOR: + errstr = "internal compressor error"; + break; + case SQFS_ERROR_INTERNAL: + errstr = "internal error"; + break; + case SQFS_ERROR_CORRUPTED: + errstr = "data corrupted"; + break; + case SQFS_ERROR_UNSUPPORTED: + errstr = "unknown or not supported"; + break; + case SQFS_ERROR_OVERFLOW: + errstr = "numeric overflow"; + break; + case SQFS_ERROR_OUT_OF_BOUNDS: + errstr = "location out of bounds"; + break; + case SFQS_ERROR_SUPER_MAGIC: + errstr = "wrong magic value in super block"; + break; + case SFQS_ERROR_SUPER_VERSION: + errstr = "wrong squashfs version in super block"; + break; + case SQFS_ERROR_SUPER_BLOCK_SIZE: + errstr = "invalid block size specified in super block"; + break; + case SQFS_ERROR_NOT_DIR: + errstr = "target is not a directory"; + break; + case SQFS_ERROR_NO_ENTRY: + errstr = "no such file or directory"; + break; + case SQFS_ERROR_LINK_LOOP: + errstr = "hard link loop detected"; + break; + case SQFS_ERROR_NOT_FILE: + errstr = "target is not a file"; + break; + case SQFS_ERROR_ARG_INVALID: + errstr = "invalid argument"; + break; + case SQFS_ERROR_SEQUENCE: + errstr = "illegal oder of operations"; + break; + default: + errstr = "libsquashfs returned an unknown error code"; + break; + } + + if (file != NULL) + fprintf(stderr, "%s: ", file); + + if (action != NULL) + fprintf(stderr, "%s: ", action); + + fprintf(stderr, "%s.\n", errstr); +} diff --git a/lib/common/src/print_size.c b/lib/common/src/print_size.c new file mode 100644 index 0000000..6e76805 --- /dev/null +++ b/lib/common/src/print_size.c @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * print_size.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "config.h" +#include "common.h" + +void print_size(sqfs_u64 size, char *buffer, bool round_to_int) +{ + static const char *fractions = "0112334456678899"; + static const char *suffices = "kMGTPEZY"; + unsigned int fraction; + int suffix = -1; + + while (size > 1024) { + ++suffix; + fraction = size % 1024; + size /= 1024; + } + + if (suffix >= 0) { + fraction /= 64; + + if (round_to_int) { + size = fraction >= 8 ? (size + 1) : size; + + sprintf(buffer, "%u%c", (unsigned int)size, + suffices[suffix]); + } else { + sprintf(buffer, "%u.%c%c", (unsigned int)size, + fractions[fraction], suffices[suffix]); + } + } else { + sprintf(buffer, "%u", (unsigned int)size); + } +} diff --git a/lib/common/src/print_version.c b/lib/common/src/print_version.c new file mode 100644 index 0000000..0c7fe5c --- /dev/null +++ b/lib/common/src/print_version.c @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * print_version.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "common.h" + +#include + +#define LICENSE_SHORT "GPLv3+" +#define LICENSE_LONG "GNU GPL version 3 or later" +#define LICENSE_URL "https://gnu.org/licenses/gpl.html" + +static const char *version_string = +"%s (%s) %s\n" +"Copyright (c) 2019 David Oberhollenzer et al\n" +"License " LICENSE_SHORT ": " LICENSE_LONG " <" LICENSE_URL ">.\n" +"This is free software: you are free to change and redistribute it.\n" +"There is NO WARRANTY, to the extent permitted by law.\n"; + +void print_version(const char *progname) +{ + printf(version_string, progname, PACKAGE_NAME, PACKAGE_VERSION); +} diff --git a/lib/common/src/writer/cleanup.c b/lib/common/src/writer/cleanup.c new file mode 100644 index 0000000..a3fd039 --- /dev/null +++ b/lib/common/src/writer/cleanup.c @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * cleanup.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "simple_writer.h" + +#include + +void sqfs_writer_cleanup(sqfs_writer_t *sqfs, int status) +{ + sqfs_drop(sqfs->xwr); + sqfs_drop(sqfs->dirwr); + sqfs_drop(sqfs->dm); + sqfs_drop(sqfs->im); + sqfs_drop(sqfs->idtbl); + sqfs_drop(sqfs->data); + sqfs_drop(sqfs->blkwr); + sqfs_drop(sqfs->fragtbl); + sqfs_drop(sqfs->cmp); + sqfs_drop(sqfs->uncmp); + fstree_cleanup(&sqfs->fs); + sqfs_drop(sqfs->outfile); + + if (status != EXIT_SUCCESS) { +#if defined(_WIN32) || defined(__WINDOWS__) + WCHAR *path = path_to_windows(sqfs->filename); + + if (path != NULL) + DeleteFileW(path); + + free(path); +#else + unlink(sqfs->filename); +#endif + } +} + diff --git a/lib/common/src/writer/finish.c b/lib/common/src/writer/finish.c new file mode 100644 index 0000000..c539579 --- /dev/null +++ b/lib/common/src/writer/finish.c @@ -0,0 +1,176 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * finish.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "simple_writer.h" +#include "common.h" + +#include + +static void print_statistics(const sqfs_super_t *super, + const sqfs_block_processor_t *blk, + const sqfs_block_writer_t *wr) +{ + const sqfs_block_processor_stats_t *proc_stats; + sqfs_u64 bytes_written, blocks_written; + char read_sz[32], written_sz[32]; + size_t ratio; + + proc_stats = sqfs_block_processor_get_stats(blk); + blocks_written = wr->get_block_count(wr); + + bytes_written = super->inode_table_start - sizeof(*super); + + if (proc_stats->input_bytes_read > 0) { + ratio = (100 * bytes_written) / proc_stats->input_bytes_read; + } else { + ratio = 100; + } + + print_size(proc_stats->input_bytes_read, read_sz, false); + print_size(bytes_written, written_sz, false); + + fputs("---------------------------------------------------\n", stdout); + printf("Data bytes read: %s\n", read_sz); + printf("Data bytes written: %s\n", written_sz); + printf("Data compression ratio: " PRI_SZ "%%\n", ratio); + fputc('\n', stdout); + + printf("Data blocks written: " PRI_U64 "\n", blocks_written); + printf("Out of which were fragment blocks: " PRI_U64 "\n", + proc_stats->frag_block_count); + + printf("Duplicate blocks omitted: " PRI_U64 "\n", + proc_stats->data_block_count + proc_stats->frag_block_count - + blocks_written); + + printf("Sparse blocks omitted: " PRI_U64 "\n", + proc_stats->sparse_block_count); + fputc('\n', stdout); + + printf("Fragments actually written: " PRI_U64 "\n", + proc_stats->actual_frag_count); + printf("Duplicated fragments omitted: " PRI_U64 "\n", + proc_stats->total_frag_count - proc_stats->actual_frag_count); + printf("Total number of inodes: %u\n", super->inode_count); + printf("Number of unique group/user IDs: %u\n", super->id_count); + fputc('\n', stdout); +} + +static int padd_sqfs(sqfs_file_t *file, sqfs_u64 size, size_t blocksize) +{ + size_t padd_sz = size % blocksize; + int status = -1; + sqfs_u8 *buffer; + + if (padd_sz == 0) + return 0; + + padd_sz = blocksize - padd_sz; + + buffer = calloc(1, padd_sz); + if (buffer == NULL) + goto fail_errno; + + if (file->write_at(file, file->get_size(file), + buffer, padd_sz)) { + goto fail_errno; + } + + status = 0; +out: + free(buffer); + return status; +fail_errno: + perror("padding output file to block size"); + goto out; +} + +int sqfs_writer_finish(sqfs_writer_t *sqfs, const sqfs_writer_cfg_t *cfg) +{ + int ret; + + if (!cfg->quiet) + fputs("Waiting for remaining data blocks...\n", stdout); + + ret = sqfs_block_processor_finish(sqfs->data); + if (ret) { + sqfs_perror(cfg->filename, "finishing data blocks", ret); + return -1; + } + + if (!cfg->quiet) + fputs("Writing inodes and directories...\n", stdout); + + sqfs->super.inode_count = sqfs->fs.unique_inode_count; + + if (sqfs_serialize_fstree(cfg->filename, sqfs)) + return -1; + + if (!cfg->quiet) + fputs("Writing fragment table...\n", stdout); + + ret = sqfs_frag_table_write(sqfs->fragtbl, sqfs->outfile, + &sqfs->super, sqfs->cmp); + if (ret) { + sqfs_perror(cfg->filename, "writing fragment table", ret); + return -1; + } + + if (cfg->exportable) { + if (!cfg->quiet) + fputs("Writing export table...\n", stdout); + + + ret = sqfs_dir_writer_write_export_table(sqfs->dirwr, + sqfs->outfile, sqfs->cmp, + sqfs->fs.root->inode_num, + sqfs->fs.root->inode_ref, + &sqfs->super); + if (ret) + return -1; + } + + if (!cfg->quiet) + fputs("Writing ID table...\n", stdout); + + ret = sqfs_id_table_write(sqfs->idtbl, sqfs->outfile, + &sqfs->super, sqfs->cmp); + if (ret) { + sqfs_perror(cfg->filename, "writing ID table", ret); + return -1; + } + + if (!cfg->no_xattr) { + if (!cfg->quiet) + fputs("Writing extended attributes...\n", stdout); + + ret = sqfs_xattr_writer_flush(sqfs->xwr, sqfs->outfile, + &sqfs->super, sqfs->cmp); + if (ret) { + sqfs_perror(cfg->filename, + "writing extended attributes", ret); + return -1; + } + } + + sqfs->super.bytes_used = sqfs->outfile->get_size(sqfs->outfile); + + ret = sqfs_super_write(&sqfs->super, sqfs->outfile); + if (ret) { + sqfs_perror(cfg->filename, "updating super block", ret); + return -1; + } + + if (padd_sqfs(sqfs->outfile, sqfs->super.bytes_used, + cfg->devblksize)) { + return -1; + } + + if (!cfg->quiet) + print_statistics(&sqfs->super, sqfs->data, sqfs->blkwr); + + return 0; +} diff --git a/lib/common/src/writer/init.c b/lib/common/src/writer/init.c new file mode 100644 index 0000000..497fc6e --- /dev/null +++ b/lib/common/src/writer/init.c @@ -0,0 +1,218 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * init.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "simple_writer.h" +#include "compress_cli.h" +#include "common.h" + +#include +#include +#include + +#ifdef HAVE_SCHED_GETAFFINITY +#include + +static size_t os_get_num_jobs(void) +{ + cpu_set_t cpu_set; + CPU_ZERO(&cpu_set); + + if (sched_getaffinity(0, sizeof cpu_set, &cpu_set) == -1) + return 1; + else + return CPU_COUNT(&cpu_set); +} +#else +static size_t os_get_num_jobs(void) +{ + return 1; +} +#endif + +void sqfs_writer_cfg_init(sqfs_writer_cfg_t *cfg) +{ + memset(cfg, 0, sizeof(*cfg)); + + cfg->num_jobs = os_get_num_jobs(); + cfg->block_size = SQFS_DEFAULT_BLOCK_SIZE; + cfg->devblksize = SQFS_DEVBLK_SIZE; + cfg->comp_id = compressor_get_default(); +} + +int sqfs_writer_init(sqfs_writer_t *sqfs, const sqfs_writer_cfg_t *wrcfg) +{ + sqfs_block_processor_desc_t blkdesc; + sqfs_compressor_config_t cfg; + int ret, flags; + + sqfs->filename = wrcfg->filename; + + if (compressor_cfg_init_options(&cfg, wrcfg->comp_id, + wrcfg->block_size, + wrcfg->comp_extra)) { + return -1; + } + + sqfs->outfile = sqfs_open_file(wrcfg->filename, wrcfg->outmode); + if (sqfs->outfile == NULL) { + perror(wrcfg->filename); + return -1; + } + + if (fstree_init(&sqfs->fs, wrcfg->fs_defaults)) + goto fail_file; + + ret = sqfs_compressor_create(&cfg, &sqfs->cmp); + +#ifdef WITH_LZO + if (cfg.id == SQFS_COMP_LZO) { + if (sqfs->cmp != NULL) + sqfs_drop(sqfs->cmp); + + ret = lzo_compressor_create(&cfg, &sqfs->cmp); + } +#endif + + if (ret != 0) { + sqfs_perror(wrcfg->filename, "creating compressor", ret); + goto fail_fs; + } + + cfg.flags |= SQFS_COMP_FLAG_UNCOMPRESS; + ret = sqfs_compressor_create(&cfg, &sqfs->uncmp); + +#ifdef WITH_LZO + if (cfg.id == SQFS_COMP_LZO) { + if (ret == 0 && sqfs->uncmp != NULL) + sqfs_drop(sqfs->uncmp); + + ret = lzo_compressor_create(&cfg, &sqfs->uncmp); + } +#endif + + if (ret != 0) { + sqfs_perror(wrcfg->filename, "creating uncompressor", ret); + goto fail_cmp; + } + + ret = sqfs_super_init(&sqfs->super, wrcfg->block_size, + sqfs->fs.defaults.st_mtime, wrcfg->comp_id); + if (ret) { + sqfs_perror(wrcfg->filename, "initializing super block", ret); + goto fail_uncmp; + } + + ret = sqfs_super_write(&sqfs->super, sqfs->outfile); + if (ret) { + sqfs_perror(wrcfg->filename, "writing super block", ret); + goto fail_uncmp; + } + + ret = sqfs->cmp->write_options(sqfs->cmp, sqfs->outfile); + if (ret < 0) { + sqfs_perror(wrcfg->filename, "writing compressor options", ret); + goto fail_uncmp; + } + + if (ret > 0) + sqfs->super.flags |= SQFS_FLAG_COMPRESSOR_OPTIONS; + + sqfs->blkwr = sqfs_block_writer_create(sqfs->outfile, + wrcfg->devblksize, 0); + if (sqfs->blkwr == NULL) { + perror("creating block writer"); + goto fail_uncmp; + } + + sqfs->fragtbl = sqfs_frag_table_create(0); + if (sqfs->fragtbl == NULL) { + perror("creating fragment table"); + goto fail_blkwr; + } + + memset(&blkdesc, 0, sizeof(blkdesc)); + blkdesc.size = sizeof(blkdesc); + blkdesc.max_block_size = wrcfg->block_size; + blkdesc.num_workers = wrcfg->num_jobs; + blkdesc.max_backlog = wrcfg->max_backlog; + blkdesc.cmp = sqfs->cmp; + blkdesc.wr = sqfs->blkwr; + blkdesc.tbl = sqfs->fragtbl; + blkdesc.file = sqfs->outfile; + blkdesc.uncmp = sqfs->uncmp; + + ret = sqfs_block_processor_create_ex(&blkdesc, &sqfs->data); + if (ret != 0) { + sqfs_perror(wrcfg->filename, "creating data block processor", + ret); + goto fail_fragtbl; + } + + sqfs->idtbl = sqfs_id_table_create(0); + if (sqfs->idtbl == NULL) { + sqfs_perror(wrcfg->filename, "creating ID table", + SQFS_ERROR_ALLOC); + goto fail_data; + } + + if (!wrcfg->no_xattr) { + sqfs->xwr = sqfs_xattr_writer_create(0); + + if (sqfs->xwr == NULL) { + sqfs_perror(wrcfg->filename, "creating xattr writer", + SQFS_ERROR_ALLOC); + goto fail_id; + } + } + + sqfs->im = sqfs_meta_writer_create(sqfs->outfile, sqfs->cmp, 0); + if (sqfs->im == NULL) { + fputs("Error creating inode meta data writer.\n", stderr); + goto fail_xwr; + } + + sqfs->dm = sqfs_meta_writer_create(sqfs->outfile, sqfs->cmp, + SQFS_META_WRITER_KEEP_IN_MEMORY); + if (sqfs->dm == NULL) { + fputs("Error creating directory meta data writer.\n", stderr); + goto fail_im; + } + + flags = 0; + if (wrcfg->exportable) + flags |= SQFS_DIR_WRITER_CREATE_EXPORT_TABLE; + + sqfs->dirwr = sqfs_dir_writer_create(sqfs->dm, flags); + if (sqfs->dirwr == NULL) { + fputs("Error creating directory table writer.\n", stderr); + goto fail_dm; + } + + return 0; +fail_dm: + sqfs_drop(sqfs->dm); +fail_im: + sqfs_drop(sqfs->im); +fail_xwr: + sqfs_drop(sqfs->xwr); +fail_id: + sqfs_drop(sqfs->idtbl); +fail_data: + sqfs_drop(sqfs->data); +fail_fragtbl: + sqfs_drop(sqfs->fragtbl); +fail_blkwr: + sqfs_drop(sqfs->blkwr); +fail_uncmp: + sqfs_drop(sqfs->uncmp); +fail_cmp: + sqfs_drop(sqfs->cmp); +fail_fs: + fstree_cleanup(&sqfs->fs); +fail_file: + sqfs_drop(sqfs->outfile); + return -1; +} diff --git a/lib/common/src/writer/serialize_fstree.c b/lib/common/src/writer/serialize_fstree.c new file mode 100644 index 0000000..9776874 --- /dev/null +++ b/lib/common/src/writer/serialize_fstree.c @@ -0,0 +1,202 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * serialize_fstree.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "common.h" + +#include +#include +#include +#include + +static sqfs_inode_generic_t *tree_node_to_inode(tree_node_t *node) +{ + sqfs_inode_generic_t *inode; + size_t extra = 0; + + if (S_ISLNK(node->mode)) + extra = strlen(node->data.target); + + inode = calloc(1, sizeof(*inode) + extra); + if (inode == NULL) { + perror("creating inode"); + return NULL; + } + + switch (node->mode & S_IFMT) { + case S_IFSOCK: + inode->base.type = SQFS_INODE_SOCKET; + inode->data.ipc.nlink = node->link_count; + break; + case S_IFIFO: + inode->base.type = SQFS_INODE_FIFO; + inode->data.ipc.nlink = node->link_count; + break; + case S_IFLNK: + inode->base.type = SQFS_INODE_SLINK; + inode->data.slink.nlink = node->link_count; + inode->data.slink.target_size = extra; + memcpy(inode->extra, node->data.target, extra); + break; + case S_IFBLK: + inode->base.type = SQFS_INODE_BDEV; + inode->data.dev.nlink = node->link_count; + inode->data.dev.devno = node->data.devno; + break; + case S_IFCHR: + inode->base.type = SQFS_INODE_CDEV; + inode->data.dev.nlink = node->link_count; + inode->data.dev.devno = node->data.devno; + break; + default: + assert(0); + } + + return inode; +} + +static sqfs_inode_generic_t *write_dir_entries(const char *filename, + sqfs_dir_writer_t *dirw, + tree_node_t *node) +{ + sqfs_u32 xattr, parent_inode; + sqfs_inode_generic_t *inode; + tree_node_t *it, *tgt; + int ret; + + ret = sqfs_dir_writer_begin(dirw, 0); + if (ret) + goto fail; + + for (it = node->data.dir.children; it != NULL; it = it->next) { + if (it->mode == FSTREE_MODE_HARD_LINK_RESOLVED) { + tgt = it->data.target_node; + } else { + tgt = it; + } + + ret = sqfs_dir_writer_add_entry(dirw, it->name, tgt->inode_num, + tgt->inode_ref, tgt->mode); + if (ret) + goto fail; + } + + ret = sqfs_dir_writer_end(dirw); + if (ret) + goto fail; + + xattr = node->xattr_idx; + parent_inode = (node->parent == NULL) ? 0 : node->parent->inode_num; + + inode = sqfs_dir_writer_create_inode(dirw, 0, xattr, parent_inode); + if (inode == NULL) { + ret = SQFS_ERROR_ALLOC; + goto fail; + } + + if (inode->base.type == SQFS_INODE_DIR) { + inode->data.dir.nlink = node->link_count; + } else { + inode->data.dir_ext.nlink = node->link_count; + } + + return inode; +fail: + sqfs_perror(filename, "recoding directory entries", ret); + return NULL; +} + +static int serialize_tree_node(const char *filename, sqfs_writer_t *wr, + tree_node_t *n) +{ + sqfs_inode_generic_t *inode; + sqfs_u32 offset; + sqfs_u64 block; + int ret; + + if (S_ISDIR(n->mode)) { + inode = write_dir_entries(filename, wr->dirwr, n); + ret = SQFS_ERROR_INTERNAL; + } else if (S_ISREG(n->mode)) { + inode = n->data.file.inode; + n->data.file.inode = NULL; + ret = SQFS_ERROR_INTERNAL; + + if (inode->base.type == SQFS_INODE_FILE && n->link_count > 1) { + sqfs_inode_make_extended(inode); + inode->data.file_ext.nlink = n->link_count; + } else { + inode->data.file_ext.nlink = n->link_count; + } + } else { + inode = tree_node_to_inode(n); + ret = SQFS_ERROR_ALLOC; + } + + if (inode == NULL) + return ret; + + inode->base.mode = n->mode; + inode->base.mod_time = n->mod_time; + inode->base.inode_number = n->inode_num; + + sqfs_inode_set_xattr_index(inode, n->xattr_idx); + + if (n->xattr_idx == 0xFFFFFFFF && !S_ISDIR(n->mode)) + sqfs_inode_make_basic(inode); + + ret = sqfs_id_table_id_to_index(wr->idtbl, n->uid, + &inode->base.uid_idx); + if (ret) + goto out; + + ret = sqfs_id_table_id_to_index(wr->idtbl, n->gid, + &inode->base.gid_idx); + if (ret) + goto out; + + sqfs_meta_writer_get_position(wr->im, &block, &offset); + n->inode_ref = (block << 16) | offset; + + ret = sqfs_meta_writer_write_inode(wr->im, inode); +out: + free(inode); + return ret; +} + +int sqfs_serialize_fstree(const char *filename, sqfs_writer_t *wr) +{ + size_t i; + int ret; + + wr->super.inode_table_start = wr->outfile->get_size(wr->outfile); + + for (i = 0; i < wr->fs.unique_inode_count; ++i) { + ret = serialize_tree_node(filename, wr, wr->fs.inodes[i]); + if (ret) + goto out; + } + + ret = sqfs_meta_writer_flush(wr->im); + if (ret) + goto out; + + ret = sqfs_meta_writer_flush(wr->dm); + if (ret) + goto out; + + wr->super.root_inode_ref = wr->fs.root->inode_ref; + wr->super.directory_table_start = wr->outfile->get_size(wr->outfile); + + ret = sqfs_meta_write_write_to_file(wr->dm); + if (ret) + goto out; + + ret = 0; +out: + if (ret) + sqfs_perror(filename, "storing filesystem tree", ret); + return ret; +} diff --git a/lib/common/writer/cleanup.c b/lib/common/writer/cleanup.c deleted file mode 100644 index a3fd039..0000000 --- a/lib/common/writer/cleanup.c +++ /dev/null @@ -1,39 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * cleanup.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "simple_writer.h" - -#include - -void sqfs_writer_cleanup(sqfs_writer_t *sqfs, int status) -{ - sqfs_drop(sqfs->xwr); - sqfs_drop(sqfs->dirwr); - sqfs_drop(sqfs->dm); - sqfs_drop(sqfs->im); - sqfs_drop(sqfs->idtbl); - sqfs_drop(sqfs->data); - sqfs_drop(sqfs->blkwr); - sqfs_drop(sqfs->fragtbl); - sqfs_drop(sqfs->cmp); - sqfs_drop(sqfs->uncmp); - fstree_cleanup(&sqfs->fs); - sqfs_drop(sqfs->outfile); - - if (status != EXIT_SUCCESS) { -#if defined(_WIN32) || defined(__WINDOWS__) - WCHAR *path = path_to_windows(sqfs->filename); - - if (path != NULL) - DeleteFileW(path); - - free(path); -#else - unlink(sqfs->filename); -#endif - } -} - diff --git a/lib/common/writer/finish.c b/lib/common/writer/finish.c deleted file mode 100644 index c539579..0000000 --- a/lib/common/writer/finish.c +++ /dev/null @@ -1,176 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * finish.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "simple_writer.h" -#include "common.h" - -#include - -static void print_statistics(const sqfs_super_t *super, - const sqfs_block_processor_t *blk, - const sqfs_block_writer_t *wr) -{ - const sqfs_block_processor_stats_t *proc_stats; - sqfs_u64 bytes_written, blocks_written; - char read_sz[32], written_sz[32]; - size_t ratio; - - proc_stats = sqfs_block_processor_get_stats(blk); - blocks_written = wr->get_block_count(wr); - - bytes_written = super->inode_table_start - sizeof(*super); - - if (proc_stats->input_bytes_read > 0) { - ratio = (100 * bytes_written) / proc_stats->input_bytes_read; - } else { - ratio = 100; - } - - print_size(proc_stats->input_bytes_read, read_sz, false); - print_size(bytes_written, written_sz, false); - - fputs("---------------------------------------------------\n", stdout); - printf("Data bytes read: %s\n", read_sz); - printf("Data bytes written: %s\n", written_sz); - printf("Data compression ratio: " PRI_SZ "%%\n", ratio); - fputc('\n', stdout); - - printf("Data blocks written: " PRI_U64 "\n", blocks_written); - printf("Out of which were fragment blocks: " PRI_U64 "\n", - proc_stats->frag_block_count); - - printf("Duplicate blocks omitted: " PRI_U64 "\n", - proc_stats->data_block_count + proc_stats->frag_block_count - - blocks_written); - - printf("Sparse blocks omitted: " PRI_U64 "\n", - proc_stats->sparse_block_count); - fputc('\n', stdout); - - printf("Fragments actually written: " PRI_U64 "\n", - proc_stats->actual_frag_count); - printf("Duplicated fragments omitted: " PRI_U64 "\n", - proc_stats->total_frag_count - proc_stats->actual_frag_count); - printf("Total number of inodes: %u\n", super->inode_count); - printf("Number of unique group/user IDs: %u\n", super->id_count); - fputc('\n', stdout); -} - -static int padd_sqfs(sqfs_file_t *file, sqfs_u64 size, size_t blocksize) -{ - size_t padd_sz = size % blocksize; - int status = -1; - sqfs_u8 *buffer; - - if (padd_sz == 0) - return 0; - - padd_sz = blocksize - padd_sz; - - buffer = calloc(1, padd_sz); - if (buffer == NULL) - goto fail_errno; - - if (file->write_at(file, file->get_size(file), - buffer, padd_sz)) { - goto fail_errno; - } - - status = 0; -out: - free(buffer); - return status; -fail_errno: - perror("padding output file to block size"); - goto out; -} - -int sqfs_writer_finish(sqfs_writer_t *sqfs, const sqfs_writer_cfg_t *cfg) -{ - int ret; - - if (!cfg->quiet) - fputs("Waiting for remaining data blocks...\n", stdout); - - ret = sqfs_block_processor_finish(sqfs->data); - if (ret) { - sqfs_perror(cfg->filename, "finishing data blocks", ret); - return -1; - } - - if (!cfg->quiet) - fputs("Writing inodes and directories...\n", stdout); - - sqfs->super.inode_count = sqfs->fs.unique_inode_count; - - if (sqfs_serialize_fstree(cfg->filename, sqfs)) - return -1; - - if (!cfg->quiet) - fputs("Writing fragment table...\n", stdout); - - ret = sqfs_frag_table_write(sqfs->fragtbl, sqfs->outfile, - &sqfs->super, sqfs->cmp); - if (ret) { - sqfs_perror(cfg->filename, "writing fragment table", ret); - return -1; - } - - if (cfg->exportable) { - if (!cfg->quiet) - fputs("Writing export table...\n", stdout); - - - ret = sqfs_dir_writer_write_export_table(sqfs->dirwr, - sqfs->outfile, sqfs->cmp, - sqfs->fs.root->inode_num, - sqfs->fs.root->inode_ref, - &sqfs->super); - if (ret) - return -1; - } - - if (!cfg->quiet) - fputs("Writing ID table...\n", stdout); - - ret = sqfs_id_table_write(sqfs->idtbl, sqfs->outfile, - &sqfs->super, sqfs->cmp); - if (ret) { - sqfs_perror(cfg->filename, "writing ID table", ret); - return -1; - } - - if (!cfg->no_xattr) { - if (!cfg->quiet) - fputs("Writing extended attributes...\n", stdout); - - ret = sqfs_xattr_writer_flush(sqfs->xwr, sqfs->outfile, - &sqfs->super, sqfs->cmp); - if (ret) { - sqfs_perror(cfg->filename, - "writing extended attributes", ret); - return -1; - } - } - - sqfs->super.bytes_used = sqfs->outfile->get_size(sqfs->outfile); - - ret = sqfs_super_write(&sqfs->super, sqfs->outfile); - if (ret) { - sqfs_perror(cfg->filename, "updating super block", ret); - return -1; - } - - if (padd_sqfs(sqfs->outfile, sqfs->super.bytes_used, - cfg->devblksize)) { - return -1; - } - - if (!cfg->quiet) - print_statistics(&sqfs->super, sqfs->data, sqfs->blkwr); - - return 0; -} diff --git a/lib/common/writer/init.c b/lib/common/writer/init.c deleted file mode 100644 index 497fc6e..0000000 --- a/lib/common/writer/init.c +++ /dev/null @@ -1,218 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * init.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "simple_writer.h" -#include "compress_cli.h" -#include "common.h" - -#include -#include -#include - -#ifdef HAVE_SCHED_GETAFFINITY -#include - -static size_t os_get_num_jobs(void) -{ - cpu_set_t cpu_set; - CPU_ZERO(&cpu_set); - - if (sched_getaffinity(0, sizeof cpu_set, &cpu_set) == -1) - return 1; - else - return CPU_COUNT(&cpu_set); -} -#else -static size_t os_get_num_jobs(void) -{ - return 1; -} -#endif - -void sqfs_writer_cfg_init(sqfs_writer_cfg_t *cfg) -{ - memset(cfg, 0, sizeof(*cfg)); - - cfg->num_jobs = os_get_num_jobs(); - cfg->block_size = SQFS_DEFAULT_BLOCK_SIZE; - cfg->devblksize = SQFS_DEVBLK_SIZE; - cfg->comp_id = compressor_get_default(); -} - -int sqfs_writer_init(sqfs_writer_t *sqfs, const sqfs_writer_cfg_t *wrcfg) -{ - sqfs_block_processor_desc_t blkdesc; - sqfs_compressor_config_t cfg; - int ret, flags; - - sqfs->filename = wrcfg->filename; - - if (compressor_cfg_init_options(&cfg, wrcfg->comp_id, - wrcfg->block_size, - wrcfg->comp_extra)) { - return -1; - } - - sqfs->outfile = sqfs_open_file(wrcfg->filename, wrcfg->outmode); - if (sqfs->outfile == NULL) { - perror(wrcfg->filename); - return -1; - } - - if (fstree_init(&sqfs->fs, wrcfg->fs_defaults)) - goto fail_file; - - ret = sqfs_compressor_create(&cfg, &sqfs->cmp); - -#ifdef WITH_LZO - if (cfg.id == SQFS_COMP_LZO) { - if (sqfs->cmp != NULL) - sqfs_drop(sqfs->cmp); - - ret = lzo_compressor_create(&cfg, &sqfs->cmp); - } -#endif - - if (ret != 0) { - sqfs_perror(wrcfg->filename, "creating compressor", ret); - goto fail_fs; - } - - cfg.flags |= SQFS_COMP_FLAG_UNCOMPRESS; - ret = sqfs_compressor_create(&cfg, &sqfs->uncmp); - -#ifdef WITH_LZO - if (cfg.id == SQFS_COMP_LZO) { - if (ret == 0 && sqfs->uncmp != NULL) - sqfs_drop(sqfs->uncmp); - - ret = lzo_compressor_create(&cfg, &sqfs->uncmp); - } -#endif - - if (ret != 0) { - sqfs_perror(wrcfg->filename, "creating uncompressor", ret); - goto fail_cmp; - } - - ret = sqfs_super_init(&sqfs->super, wrcfg->block_size, - sqfs->fs.defaults.st_mtime, wrcfg->comp_id); - if (ret) { - sqfs_perror(wrcfg->filename, "initializing super block", ret); - goto fail_uncmp; - } - - ret = sqfs_super_write(&sqfs->super, sqfs->outfile); - if (ret) { - sqfs_perror(wrcfg->filename, "writing super block", ret); - goto fail_uncmp; - } - - ret = sqfs->cmp->write_options(sqfs->cmp, sqfs->outfile); - if (ret < 0) { - sqfs_perror(wrcfg->filename, "writing compressor options", ret); - goto fail_uncmp; - } - - if (ret > 0) - sqfs->super.flags |= SQFS_FLAG_COMPRESSOR_OPTIONS; - - sqfs->blkwr = sqfs_block_writer_create(sqfs->outfile, - wrcfg->devblksize, 0); - if (sqfs->blkwr == NULL) { - perror("creating block writer"); - goto fail_uncmp; - } - - sqfs->fragtbl = sqfs_frag_table_create(0); - if (sqfs->fragtbl == NULL) { - perror("creating fragment table"); - goto fail_blkwr; - } - - memset(&blkdesc, 0, sizeof(blkdesc)); - blkdesc.size = sizeof(blkdesc); - blkdesc.max_block_size = wrcfg->block_size; - blkdesc.num_workers = wrcfg->num_jobs; - blkdesc.max_backlog = wrcfg->max_backlog; - blkdesc.cmp = sqfs->cmp; - blkdesc.wr = sqfs->blkwr; - blkdesc.tbl = sqfs->fragtbl; - blkdesc.file = sqfs->outfile; - blkdesc.uncmp = sqfs->uncmp; - - ret = sqfs_block_processor_create_ex(&blkdesc, &sqfs->data); - if (ret != 0) { - sqfs_perror(wrcfg->filename, "creating data block processor", - ret); - goto fail_fragtbl; - } - - sqfs->idtbl = sqfs_id_table_create(0); - if (sqfs->idtbl == NULL) { - sqfs_perror(wrcfg->filename, "creating ID table", - SQFS_ERROR_ALLOC); - goto fail_data; - } - - if (!wrcfg->no_xattr) { - sqfs->xwr = sqfs_xattr_writer_create(0); - - if (sqfs->xwr == NULL) { - sqfs_perror(wrcfg->filename, "creating xattr writer", - SQFS_ERROR_ALLOC); - goto fail_id; - } - } - - sqfs->im = sqfs_meta_writer_create(sqfs->outfile, sqfs->cmp, 0); - if (sqfs->im == NULL) { - fputs("Error creating inode meta data writer.\n", stderr); - goto fail_xwr; - } - - sqfs->dm = sqfs_meta_writer_create(sqfs->outfile, sqfs->cmp, - SQFS_META_WRITER_KEEP_IN_MEMORY); - if (sqfs->dm == NULL) { - fputs("Error creating directory meta data writer.\n", stderr); - goto fail_im; - } - - flags = 0; - if (wrcfg->exportable) - flags |= SQFS_DIR_WRITER_CREATE_EXPORT_TABLE; - - sqfs->dirwr = sqfs_dir_writer_create(sqfs->dm, flags); - if (sqfs->dirwr == NULL) { - fputs("Error creating directory table writer.\n", stderr); - goto fail_dm; - } - - return 0; -fail_dm: - sqfs_drop(sqfs->dm); -fail_im: - sqfs_drop(sqfs->im); -fail_xwr: - sqfs_drop(sqfs->xwr); -fail_id: - sqfs_drop(sqfs->idtbl); -fail_data: - sqfs_drop(sqfs->data); -fail_fragtbl: - sqfs_drop(sqfs->fragtbl); -fail_blkwr: - sqfs_drop(sqfs->blkwr); -fail_uncmp: - sqfs_drop(sqfs->uncmp); -fail_cmp: - sqfs_drop(sqfs->cmp); -fail_fs: - fstree_cleanup(&sqfs->fs); -fail_file: - sqfs_drop(sqfs->outfile); - return -1; -} diff --git a/lib/common/writer/serialize_fstree.c b/lib/common/writer/serialize_fstree.c deleted file mode 100644 index 9776874..0000000 --- a/lib/common/writer/serialize_fstree.c +++ /dev/null @@ -1,202 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * serialize_fstree.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "common.h" - -#include -#include -#include -#include - -static sqfs_inode_generic_t *tree_node_to_inode(tree_node_t *node) -{ - sqfs_inode_generic_t *inode; - size_t extra = 0; - - if (S_ISLNK(node->mode)) - extra = strlen(node->data.target); - - inode = calloc(1, sizeof(*inode) + extra); - if (inode == NULL) { - perror("creating inode"); - return NULL; - } - - switch (node->mode & S_IFMT) { - case S_IFSOCK: - inode->base.type = SQFS_INODE_SOCKET; - inode->data.ipc.nlink = node->link_count; - break; - case S_IFIFO: - inode->base.type = SQFS_INODE_FIFO; - inode->data.ipc.nlink = node->link_count; - break; - case S_IFLNK: - inode->base.type = SQFS_INODE_SLINK; - inode->data.slink.nlink = node->link_count; - inode->data.slink.target_size = extra; - memcpy(inode->extra, node->data.target, extra); - break; - case S_IFBLK: - inode->base.type = SQFS_INODE_BDEV; - inode->data.dev.nlink = node->link_count; - inode->data.dev.devno = node->data.devno; - break; - case S_IFCHR: - inode->base.type = SQFS_INODE_CDEV; - inode->data.dev.nlink = node->link_count; - inode->data.dev.devno = node->data.devno; - break; - default: - assert(0); - } - - return inode; -} - -static sqfs_inode_generic_t *write_dir_entries(const char *filename, - sqfs_dir_writer_t *dirw, - tree_node_t *node) -{ - sqfs_u32 xattr, parent_inode; - sqfs_inode_generic_t *inode; - tree_node_t *it, *tgt; - int ret; - - ret = sqfs_dir_writer_begin(dirw, 0); - if (ret) - goto fail; - - for (it = node->data.dir.children; it != NULL; it = it->next) { - if (it->mode == FSTREE_MODE_HARD_LINK_RESOLVED) { - tgt = it->data.target_node; - } else { - tgt = it; - } - - ret = sqfs_dir_writer_add_entry(dirw, it->name, tgt->inode_num, - tgt->inode_ref, tgt->mode); - if (ret) - goto fail; - } - - ret = sqfs_dir_writer_end(dirw); - if (ret) - goto fail; - - xattr = node->xattr_idx; - parent_inode = (node->parent == NULL) ? 0 : node->parent->inode_num; - - inode = sqfs_dir_writer_create_inode(dirw, 0, xattr, parent_inode); - if (inode == NULL) { - ret = SQFS_ERROR_ALLOC; - goto fail; - } - - if (inode->base.type == SQFS_INODE_DIR) { - inode->data.dir.nlink = node->link_count; - } else { - inode->data.dir_ext.nlink = node->link_count; - } - - return inode; -fail: - sqfs_perror(filename, "recoding directory entries", ret); - return NULL; -} - -static int serialize_tree_node(const char *filename, sqfs_writer_t *wr, - tree_node_t *n) -{ - sqfs_inode_generic_t *inode; - sqfs_u32 offset; - sqfs_u64 block; - int ret; - - if (S_ISDIR(n->mode)) { - inode = write_dir_entries(filename, wr->dirwr, n); - ret = SQFS_ERROR_INTERNAL; - } else if (S_ISREG(n->mode)) { - inode = n->data.file.inode; - n->data.file.inode = NULL; - ret = SQFS_ERROR_INTERNAL; - - if (inode->base.type == SQFS_INODE_FILE && n->link_count > 1) { - sqfs_inode_make_extended(inode); - inode->data.file_ext.nlink = n->link_count; - } else { - inode->data.file_ext.nlink = n->link_count; - } - } else { - inode = tree_node_to_inode(n); - ret = SQFS_ERROR_ALLOC; - } - - if (inode == NULL) - return ret; - - inode->base.mode = n->mode; - inode->base.mod_time = n->mod_time; - inode->base.inode_number = n->inode_num; - - sqfs_inode_set_xattr_index(inode, n->xattr_idx); - - if (n->xattr_idx == 0xFFFFFFFF && !S_ISDIR(n->mode)) - sqfs_inode_make_basic(inode); - - ret = sqfs_id_table_id_to_index(wr->idtbl, n->uid, - &inode->base.uid_idx); - if (ret) - goto out; - - ret = sqfs_id_table_id_to_index(wr->idtbl, n->gid, - &inode->base.gid_idx); - if (ret) - goto out; - - sqfs_meta_writer_get_position(wr->im, &block, &offset); - n->inode_ref = (block << 16) | offset; - - ret = sqfs_meta_writer_write_inode(wr->im, inode); -out: - free(inode); - return ret; -} - -int sqfs_serialize_fstree(const char *filename, sqfs_writer_t *wr) -{ - size_t i; - int ret; - - wr->super.inode_table_start = wr->outfile->get_size(wr->outfile); - - for (i = 0; i < wr->fs.unique_inode_count; ++i) { - ret = serialize_tree_node(filename, wr, wr->fs.inodes[i]); - if (ret) - goto out; - } - - ret = sqfs_meta_writer_flush(wr->im); - if (ret) - goto out; - - ret = sqfs_meta_writer_flush(wr->dm); - if (ret) - goto out; - - wr->super.root_inode_ref = wr->fs.root->inode_ref; - wr->super.directory_table_start = wr->outfile->get_size(wr->outfile); - - ret = sqfs_meta_write_write_to_file(wr->dm); - if (ret) - goto out; - - ret = 0; -out: - if (ret) - sqfs_perror(filename, "storing filesystem tree", ret); - return ret; -} diff --git a/lib/compat/Makemodule.am b/lib/compat/Makemodule.am index 06fc95e..be9bf8c 100644 --- a/lib/compat/Makemodule.am +++ b/lib/compat/Makemodule.am @@ -1,13 +1,9 @@ -libcompat_a_SOURCES = lib/compat/getsubopt.c -libcompat_a_SOURCES += lib/compat/strndup.c lib/compat/mockups.c -libcompat_a_SOURCES += lib/compat/chdir.c include/compat.h -libcompat_a_SOURCES += lib/compat/path_to_windows.c -libcompat_a_SOURCES += lib/compat/w32_perror.c -libcompat_a_SOURCES += lib/compat/w32_wmain.c -libcompat_a_SOURCES += lib/compat/w32_stdio.c -libcompat_a_SOURCES += lib/compat/fnmatch.c -libcompat_a_SOURCES += lib/compat/getopt.c -libcompat_a_SOURCES += lib/compat/getopt_long.c -libcompat_a_SOURCES += lib/compat/strchrnul.c +libcompat_a_SOURCES = include/compat.h lib/compat/src/getsubopt.c \ + lib/compat/src/strndup.c lib/compat/src/mockups.c \ + lib/compat/src/chdir.c lib/compat/src/path_to_windows.c \ + lib/compat/src/w32_perror.c lib/compat/src/w32_wmain.c \ + lib/compat/src/w32_stdio.c lib/compat/src/fnmatch.c \ + lib/compat/src/getopt.c lib/compat/src/getopt_long.c \ + lib/compat/src/strchrnul.c noinst_LIBRARIES += libcompat.a diff --git a/lib/compat/chdir.c b/lib/compat/chdir.c deleted file mode 100644 index f695e2a..0000000 --- a/lib/compat/chdir.c +++ /dev/null @@ -1,34 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * chdir.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "config.h" -#include "compat.h" - -#ifdef _WIN32 -#include -#include - -int chdir(const char *path) -{ - WCHAR *wpath; - int ret; - - wpath = path_to_windows(path); - if (wpath == NULL) - return -1; - - if (!SetCurrentDirectoryW(wpath)) { - fprintf(stderr, "Switching to directory '%s': %ld\n", - path, GetLastError()); - ret = -1; - } else { - ret = 0; - } - - free(wpath); - return ret; -} -#endif diff --git a/lib/compat/fnmatch.c b/lib/compat/fnmatch.c deleted file mode 100644 index ed4dde1..0000000 --- a/lib/compat/fnmatch.c +++ /dev/null @@ -1,305 +0,0 @@ -/* - * An implementation of what I call the "Sea of Stars" algorithm for - * POSIX fnmatch(). The basic idea is that we factor the pattern into - * a head component (which we match first and can reject without ever - * measuring the length of the string), an optional tail component - * (which only exists if the pattern contains at least one star), and - * an optional "sea of stars", a set of star-separated components - * between the head and tail. After the head and tail matches have - * been removed from the input string, the components in the "sea of - * stars" are matched sequentially by searching for their first - * occurrence past the end of the previous match. - * - * - Rich Felker, April 2012 - */ - -#include "compat.h" - -#include -#include - -#ifndef HAVE_FNMATCH -#define END 0 -#define UNMATCHABLE -2 -#define BRACKET -3 -#define QUESTION -4 -#define STAR -5 - -static int str_next(const char *str, size_t n, size_t *step) -{ - if (!n) { - *step = 0; - return 0; - } - if (str[0] >= 128U) { - wchar_t wc; - int k = mbtowc(&wc, str, n); - if (k<0) { - *step = 1; - return -1; - } - *step = k; - return wc; - } - *step = 1; - return str[0]; -} - -static int pat_next(const char *pat, size_t m, size_t *step) -{ - int esc = 0; - if (!m || !*pat) { - *step = 0; - return END; - } - *step = 1; - if (pat[0]=='\\' && pat[1]) { - *step = 2; - pat++; - esc = 1; - goto escaped; - } - if (pat[0]=='[') { - size_t k = 1; - if (k= 128U) { - wchar_t wc; - int k = mbtowc(&wc, pat, m); - if (k<0) { - *step = 0; - return UNMATCHABLE; - } - *step = k + esc; - return wc; - } - return pat[0]; -} - -static int match_bracket(const char *p, int k, int kfold) -{ - wchar_t wc; - int inv = 0; - p++; - if (*p=='^' || *p=='!') { - inv = 1; - p++; - } - if (*p==']') { - if (k==']') return !inv; - p++; - } else if (*p=='-') { - if (k=='-') return !inv; - p++; - } - wc = p[-1]; - for (; *p != ']'; p++) { - if (p[0]=='-' && p[1]!=']') { - wchar_t wc2; - int l = mbtowc(&wc2, p+1, 4); - if (l < 0) return 0; - if (wc <= wc2) - if ((unsigned)k-wc <= wc2-wc || - (unsigned)kfold-wc <= wc2-wc) - return !inv; - p += l-1; - continue; - } - if (p[0]=='[' && (p[1]==':' || p[1]=='.' || p[1]=='=')) { - const char *p0 = p+2; - int z = p[1]; - p+=3; - while (p[-1]!=z || p[0]!=']') p++; - if (z == ':' && p-1-p0 < 16) { - char buf[16]; - memcpy(buf, p0, p-1-p0); - buf[p-1-p0] = 0; - if (iswctype(k, wctype(buf)) || - iswctype(kfold, wctype(buf))) - return !inv; - } - continue; - } - if (*p < 128U) { - wc = (unsigned char)*p; - } else { - int l = mbtowc(&wc, p, 4); - if (l < 0) return 0; - p += l-1; - } - if (wc==k || wc==kfold) return !inv; - } - return inv; -} - -static int fnmatch_internal(const char *pat, size_t m, const char *str, size_t n) -{ - const char *p, *ptail, *endpat; - const char *s, *stail, *endstr; - size_t pinc, sinc, tailcnt=0; - int c, k, kfold; - - for (;;) { - switch ((c = pat_next(pat, m, &pinc))) { - case UNMATCHABLE: - return FNM_NOMATCH; - case STAR: - pat++; - m--; - break; - default: - k = str_next(str, n, &sinc); - if (k <= 0) - return (c==END) ? 0 : FNM_NOMATCH; - str += sinc; - n -= sinc; - kfold = k; - if (c == BRACKET) { - if (!match_bracket(pat, k, kfold)) - return FNM_NOMATCH; - } else if (c != QUESTION && k != c && kfold != c) { - return FNM_NOMATCH; - } - pat+=pinc; - m-=pinc; - continue; - } - break; - } - - /* Compute real pat length if it was initially unknown/-1 */ - m = strnlen(pat, m); - endpat = pat + m; - - /* Find the last * in pat and count chars needed after it */ - for (p=ptail=pat; pstr && tailcnt; tailcnt--) { - if (s[-1] < 128U || MB_CUR_MAX==1) s--; - else while ((unsigned char)*--s-0x80U<0x40 && s>str); - } - if (tailcnt) return FNM_NOMATCH; - stail = s; - - /* Check that the pat and str tails match */ - p = ptail; - for (;;) { - c = pat_next(p, endpat-p, &pinc); - p += pinc; - if ((k = str_next(s, endstr-s, &sinc)) <= 0) { - if (c != END) return FNM_NOMATCH; - break; - } - s += sinc; - kfold = k; - if (c == BRACKET) { - if (!match_bracket(p-pinc, k, kfold)) - return FNM_NOMATCH; - } else if (c != QUESTION && k != c && kfold != c) { - return FNM_NOMATCH; - } - } - - /* We're all done with the tails now, so throw them out */ - endstr = stail; - endpat = ptail; - - /* Match pattern components until there are none left */ - while (pat 0) str += sinc; - else for (str++; str_next(str, endstr-str, &sinc)<0; str++); - } - - return 0; -} - -int fnmatch(const char *pat, const char *str, int flags) -{ - const char *s, *p; - size_t inc; - int c; - if (flags & FNM_PATHNAME) for (;;) { - for (s=str; *s && *s!='/'; s++); - for (p=pat; (c=pat_next(p, -1, &inc))!=END && c!='/'; p+=inc); - if (c!=*s) - return FNM_NOMATCH; - if (fnmatch_internal(pat, p-pat, str, s-str)) - return FNM_NOMATCH; - if (!c) return 0; - str = s+1; - pat = p+inc; - } - return fnmatch_internal(pat, -1, str, -1); -} -#endif /* HAVE_FNMATCH */ diff --git a/lib/compat/getopt.c b/lib/compat/getopt.c deleted file mode 100644 index 9876a87..0000000 --- a/lib/compat/getopt.c +++ /dev/null @@ -1,96 +0,0 @@ -#include "compat.h" - -#include -#include -#include -#include - -#ifndef HAVE_GETOPT -char *optarg; -int optind=1, opterr=1, optopt, optpos, optreset=0; - -void __getopt_msg(const char *a, const char *b, const char *c, size_t l) -{ - fputs(a, stderr); - fwrite(b, strlen(b), 1, stderr); - fwrite(c, 1, l, stderr); - putc('\n', stderr); -} - -int getopt(int argc, char * const argv[], const char *optstring) -{ - int i; - wchar_t c, d; - int k, l; - char *optchar; - - if (!optind || __optreset) { - optreset = 0; - optpos = 0; - optind = 1; - } - - if (optind >= argc || !argv[optind]) - return -1; - - if (argv[optind][0] != '-') { - if (optstring[0] == '-') { - optarg = argv[optind++]; - return 1; - } - return -1; - } - - if (!argv[optind][1]) - return -1; - - if (argv[optind][1] == '-' && !argv[optind][2]) - return optind++, -1; - - if (!optpos) optpos++; - if ((k = mbtowc(&c, argv[optind]+optpos, MB_LEN_MAX)) < 0) { - k = 1; - c = 0xfffd; /* replacement char */ - } - optchar = argv[optind]+optpos; - optpos += k; - - if (!argv[optind][optpos]) { - optind++; - optpos = 0; - } - - if (optstring[0] == '-' || optstring[0] == '+') - optstring++; - - i = 0; - d = 0; - do { - l = mbtowc(&d, optstring+i, MB_LEN_MAX); - if (l>0) i+=l; else i++; - } while (l && d != c); - - if (d != c || c == ':') { - optopt = c; - if (optstring[0] != ':' && opterr) - __getopt_msg(argv[0], ": unrecognized option: ", optchar, k); - return '?'; - } - if (optstring[i] == ':') { - optarg = 0; - if (optstring[i+1] != ':' || optpos) { - optarg = argv[optind++] + optpos; - optpos = 0; - } - if (optind > argc) { - optopt = c; - if (optstring[0] == ':') return ':'; - if (opterr) __getopt_msg(argv[0], - ": option requires an argument: ", - optchar, k); - return '?'; - } - } - return c; -} -#endif diff --git a/lib/compat/getopt_long.c b/lib/compat/getopt_long.c deleted file mode 100644 index 58354c3..0000000 --- a/lib/compat/getopt_long.c +++ /dev/null @@ -1,95 +0,0 @@ -#include "compat.h" - -#include -#include -#include -#include -#include -#include - -#ifndef HAVE_GETOPT_LONG -static void permute(char *const *argv, int dest, int src) -{ - char **av = (char **)argv; - char *tmp = av[src]; - int i; - for (i=src; i>dest; i--) - av[i] = av[i-1]; - av[dest] = tmp; -} - -int getopt_long(int argc, char *const *argv, const char *optstring, const struct option *longopts, int *idx) -{ - optarg = 0; - if (longopts && argv[optind][0] == '-' && - (argv[optind][1] == '-' && argv[optind][2])) - { - int colon = optstring[optstring[0]=='+'||optstring[0]=='-']==':'; - int i, cnt, match; - char *arg, *opt, *start = argv[optind]+1; - for (cnt=i=0; longopts[i].name; i++) { - const char *name = longopts[i].name; - opt = start; - if (*opt == '-') opt++; - while (*opt && *opt != '=' && *opt == *name) - name++, opt++; - if (*opt && *opt != '=') continue; - arg = opt; - match = i; - if (!*name) { - cnt = 1; - break; - } - cnt++; - } - if (cnt==1) { - i = match; - opt = arg; - optind++; - if (*opt == '=') { - if (!longopts[i].has_arg) { - optopt = longopts[i].val; - if (colon || !opterr) - return '?'; - __getopt_msg(argv[0], - ": option does not take an argument: ", - longopts[i].name, - strlen(longopts[i].name)); - return '?'; - } - optarg = opt+1; - } else if (longopts[i].has_arg == required_argument) { - if (!(optarg = argv[optind])) { - optopt = longopts[i].val; - if (colon) return ':'; - if (!opterr) return '?'; - __getopt_msg(argv[0], - ": option requires an argument: ", - longopts[i].name, - strlen(longopts[i].name)); - return '?'; - } - optind++; - } - if (idx) *idx = i; - if (longopts[i].flag) { - *longopts[i].flag = longopts[i].val; - return 0; - } - return longopts[i].val; - } - if (argv[optind][1] == '-') { - optopt = 0; - if (!colon && opterr) - __getopt_msg(argv[0], cnt ? - ": option is ambiguous: " : - ": unrecognized option: ", - argv[optind]+2, - strlen(argv[optind]+2)); - optind++; - return '?'; - } - } - return getopt(argc, argv, optstring); -} -#endif diff --git a/lib/compat/getsubopt.c b/lib/compat/getsubopt.c deleted file mode 100644 index e6fea1a..0000000 --- a/lib/compat/getsubopt.c +++ /dev/null @@ -1,27 +0,0 @@ -#include "compat.h" - -#include -#include - -#ifndef HAVE_GETSUBOPT -int getsubopt(char **opt, char *const *keys, char **val) -{ - char *s = *opt; - int i; - - *val = NULL; - *opt = strchr(s, ','); - if (*opt) *(*opt)++ = 0; - else *opt = s + strlen(s); - - for (i=0; keys[i]; i++) { - size_t l = strlen(keys[i]); - if (strncmp(keys[i], s, l)) continue; - if (s[l] == '=') - *val = s + l + 1; - else if (s[l]) continue; - return i; - } - return -1; -} -#endif diff --git a/lib/compat/mockups.c b/lib/compat/mockups.c deleted file mode 100644 index 4c396ec..0000000 --- a/lib/compat/mockups.c +++ /dev/null @@ -1,51 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * mockups.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "config.h" -#include "compat.h" - -#include - -#ifdef _WIN32 -int fchownat(int dirfd, const char *path, int uid, int gid, int flags) -{ - if (dirfd != AT_FDCWD) { - fputs("[FIXME] fchownat stub only supports AT_FDCWD!\n", - stderr); - return -1; - } - - if (flags != 0 && flags != AT_SYMLINK_NOFOLLOW) { - fputs("[FIXME] fchownat stub used with an unknown flag!\n", - stderr); - return -1; - } - - (void)path; - (void)uid; - (void)gid; - return 0; -} - -int fchmodat(int dirfd, const char *path, int mode, int flags) -{ - if (dirfd != AT_FDCWD) { - fputs("[FIXME] fchmodat stub only supports AT_FDCWD!\n", - stderr); - return -1; - } - - if (flags != 0 && flags != AT_SYMLINK_NOFOLLOW) { - fputs("[FIXME] fchmodat stub used with an unknown flag!\n", - stderr); - return -1; - } - - (void)path; - (void)mode; - return 0; -} -#endif diff --git a/lib/compat/path_to_windows.c b/lib/compat/path_to_windows.c deleted file mode 100644 index ff3a5d2..0000000 --- a/lib/compat/path_to_windows.c +++ /dev/null @@ -1,43 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * path_to_windows.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "config.h" -#include "compat.h" - -#include -#include - -#if defined(_WIN32) || defined(__WINDOWS__) -WCHAR *path_to_windows(const char *input) -{ - WCHAR *wpath, *ptr; - DWORD length; - - length = MultiByteToWideChar(CP_UTF8, 0, input, -1, NULL, 0); - if (length <= 0) { - fprintf(stderr, "Converting UTF-8 path to UTF-16: %ld\n", - GetLastError()); - return NULL; - } - - wpath = calloc(sizeof(wpath[0]), length + 1); - if (wpath == NULL) { - fprintf(stderr, - "Converting UTF-8 path to UTF-16: out of memory\n"); - return NULL; - } - - MultiByteToWideChar(CP_UTF8, 0, input, -1, wpath, length + 1); - wpath[length] = '\0'; - - for (ptr = wpath; *ptr != '\0'; ++ptr) { - if (*ptr == '/') - *ptr = '\\'; - } - - return wpath; -} -#endif diff --git a/lib/compat/src/chdir.c b/lib/compat/src/chdir.c new file mode 100644 index 0000000..f695e2a --- /dev/null +++ b/lib/compat/src/chdir.c @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * chdir.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "config.h" +#include "compat.h" + +#ifdef _WIN32 +#include +#include + +int chdir(const char *path) +{ + WCHAR *wpath; + int ret; + + wpath = path_to_windows(path); + if (wpath == NULL) + return -1; + + if (!SetCurrentDirectoryW(wpath)) { + fprintf(stderr, "Switching to directory '%s': %ld\n", + path, GetLastError()); + ret = -1; + } else { + ret = 0; + } + + free(wpath); + return ret; +} +#endif diff --git a/lib/compat/src/fnmatch.c b/lib/compat/src/fnmatch.c new file mode 100644 index 0000000..ed4dde1 --- /dev/null +++ b/lib/compat/src/fnmatch.c @@ -0,0 +1,305 @@ +/* + * An implementation of what I call the "Sea of Stars" algorithm for + * POSIX fnmatch(). The basic idea is that we factor the pattern into + * a head component (which we match first and can reject without ever + * measuring the length of the string), an optional tail component + * (which only exists if the pattern contains at least one star), and + * an optional "sea of stars", a set of star-separated components + * between the head and tail. After the head and tail matches have + * been removed from the input string, the components in the "sea of + * stars" are matched sequentially by searching for their first + * occurrence past the end of the previous match. + * + * - Rich Felker, April 2012 + */ + +#include "compat.h" + +#include +#include + +#ifndef HAVE_FNMATCH +#define END 0 +#define UNMATCHABLE -2 +#define BRACKET -3 +#define QUESTION -4 +#define STAR -5 + +static int str_next(const char *str, size_t n, size_t *step) +{ + if (!n) { + *step = 0; + return 0; + } + if (str[0] >= 128U) { + wchar_t wc; + int k = mbtowc(&wc, str, n); + if (k<0) { + *step = 1; + return -1; + } + *step = k; + return wc; + } + *step = 1; + return str[0]; +} + +static int pat_next(const char *pat, size_t m, size_t *step) +{ + int esc = 0; + if (!m || !*pat) { + *step = 0; + return END; + } + *step = 1; + if (pat[0]=='\\' && pat[1]) { + *step = 2; + pat++; + esc = 1; + goto escaped; + } + if (pat[0]=='[') { + size_t k = 1; + if (k= 128U) { + wchar_t wc; + int k = mbtowc(&wc, pat, m); + if (k<0) { + *step = 0; + return UNMATCHABLE; + } + *step = k + esc; + return wc; + } + return pat[0]; +} + +static int match_bracket(const char *p, int k, int kfold) +{ + wchar_t wc; + int inv = 0; + p++; + if (*p=='^' || *p=='!') { + inv = 1; + p++; + } + if (*p==']') { + if (k==']') return !inv; + p++; + } else if (*p=='-') { + if (k=='-') return !inv; + p++; + } + wc = p[-1]; + for (; *p != ']'; p++) { + if (p[0]=='-' && p[1]!=']') { + wchar_t wc2; + int l = mbtowc(&wc2, p+1, 4); + if (l < 0) return 0; + if (wc <= wc2) + if ((unsigned)k-wc <= wc2-wc || + (unsigned)kfold-wc <= wc2-wc) + return !inv; + p += l-1; + continue; + } + if (p[0]=='[' && (p[1]==':' || p[1]=='.' || p[1]=='=')) { + const char *p0 = p+2; + int z = p[1]; + p+=3; + while (p[-1]!=z || p[0]!=']') p++; + if (z == ':' && p-1-p0 < 16) { + char buf[16]; + memcpy(buf, p0, p-1-p0); + buf[p-1-p0] = 0; + if (iswctype(k, wctype(buf)) || + iswctype(kfold, wctype(buf))) + return !inv; + } + continue; + } + if (*p < 128U) { + wc = (unsigned char)*p; + } else { + int l = mbtowc(&wc, p, 4); + if (l < 0) return 0; + p += l-1; + } + if (wc==k || wc==kfold) return !inv; + } + return inv; +} + +static int fnmatch_internal(const char *pat, size_t m, const char *str, size_t n) +{ + const char *p, *ptail, *endpat; + const char *s, *stail, *endstr; + size_t pinc, sinc, tailcnt=0; + int c, k, kfold; + + for (;;) { + switch ((c = pat_next(pat, m, &pinc))) { + case UNMATCHABLE: + return FNM_NOMATCH; + case STAR: + pat++; + m--; + break; + default: + k = str_next(str, n, &sinc); + if (k <= 0) + return (c==END) ? 0 : FNM_NOMATCH; + str += sinc; + n -= sinc; + kfold = k; + if (c == BRACKET) { + if (!match_bracket(pat, k, kfold)) + return FNM_NOMATCH; + } else if (c != QUESTION && k != c && kfold != c) { + return FNM_NOMATCH; + } + pat+=pinc; + m-=pinc; + continue; + } + break; + } + + /* Compute real pat length if it was initially unknown/-1 */ + m = strnlen(pat, m); + endpat = pat + m; + + /* Find the last * in pat and count chars needed after it */ + for (p=ptail=pat; pstr && tailcnt; tailcnt--) { + if (s[-1] < 128U || MB_CUR_MAX==1) s--; + else while ((unsigned char)*--s-0x80U<0x40 && s>str); + } + if (tailcnt) return FNM_NOMATCH; + stail = s; + + /* Check that the pat and str tails match */ + p = ptail; + for (;;) { + c = pat_next(p, endpat-p, &pinc); + p += pinc; + if ((k = str_next(s, endstr-s, &sinc)) <= 0) { + if (c != END) return FNM_NOMATCH; + break; + } + s += sinc; + kfold = k; + if (c == BRACKET) { + if (!match_bracket(p-pinc, k, kfold)) + return FNM_NOMATCH; + } else if (c != QUESTION && k != c && kfold != c) { + return FNM_NOMATCH; + } + } + + /* We're all done with the tails now, so throw them out */ + endstr = stail; + endpat = ptail; + + /* Match pattern components until there are none left */ + while (pat 0) str += sinc; + else for (str++; str_next(str, endstr-str, &sinc)<0; str++); + } + + return 0; +} + +int fnmatch(const char *pat, const char *str, int flags) +{ + const char *s, *p; + size_t inc; + int c; + if (flags & FNM_PATHNAME) for (;;) { + for (s=str; *s && *s!='/'; s++); + for (p=pat; (c=pat_next(p, -1, &inc))!=END && c!='/'; p+=inc); + if (c!=*s) + return FNM_NOMATCH; + if (fnmatch_internal(pat, p-pat, str, s-str)) + return FNM_NOMATCH; + if (!c) return 0; + str = s+1; + pat = p+inc; + } + return fnmatch_internal(pat, -1, str, -1); +} +#endif /* HAVE_FNMATCH */ diff --git a/lib/compat/src/getopt.c b/lib/compat/src/getopt.c new file mode 100644 index 0000000..9876a87 --- /dev/null +++ b/lib/compat/src/getopt.c @@ -0,0 +1,96 @@ +#include "compat.h" + +#include +#include +#include +#include + +#ifndef HAVE_GETOPT +char *optarg; +int optind=1, opterr=1, optopt, optpos, optreset=0; + +void __getopt_msg(const char *a, const char *b, const char *c, size_t l) +{ + fputs(a, stderr); + fwrite(b, strlen(b), 1, stderr); + fwrite(c, 1, l, stderr); + putc('\n', stderr); +} + +int getopt(int argc, char * const argv[], const char *optstring) +{ + int i; + wchar_t c, d; + int k, l; + char *optchar; + + if (!optind || __optreset) { + optreset = 0; + optpos = 0; + optind = 1; + } + + if (optind >= argc || !argv[optind]) + return -1; + + if (argv[optind][0] != '-') { + if (optstring[0] == '-') { + optarg = argv[optind++]; + return 1; + } + return -1; + } + + if (!argv[optind][1]) + return -1; + + if (argv[optind][1] == '-' && !argv[optind][2]) + return optind++, -1; + + if (!optpos) optpos++; + if ((k = mbtowc(&c, argv[optind]+optpos, MB_LEN_MAX)) < 0) { + k = 1; + c = 0xfffd; /* replacement char */ + } + optchar = argv[optind]+optpos; + optpos += k; + + if (!argv[optind][optpos]) { + optind++; + optpos = 0; + } + + if (optstring[0] == '-' || optstring[0] == '+') + optstring++; + + i = 0; + d = 0; + do { + l = mbtowc(&d, optstring+i, MB_LEN_MAX); + if (l>0) i+=l; else i++; + } while (l && d != c); + + if (d != c || c == ':') { + optopt = c; + if (optstring[0] != ':' && opterr) + __getopt_msg(argv[0], ": unrecognized option: ", optchar, k); + return '?'; + } + if (optstring[i] == ':') { + optarg = 0; + if (optstring[i+1] != ':' || optpos) { + optarg = argv[optind++] + optpos; + optpos = 0; + } + if (optind > argc) { + optopt = c; + if (optstring[0] == ':') return ':'; + if (opterr) __getopt_msg(argv[0], + ": option requires an argument: ", + optchar, k); + return '?'; + } + } + return c; +} +#endif diff --git a/lib/compat/src/getopt_long.c b/lib/compat/src/getopt_long.c new file mode 100644 index 0000000..58354c3 --- /dev/null +++ b/lib/compat/src/getopt_long.c @@ -0,0 +1,95 @@ +#include "compat.h" + +#include +#include +#include +#include +#include +#include + +#ifndef HAVE_GETOPT_LONG +static void permute(char *const *argv, int dest, int src) +{ + char **av = (char **)argv; + char *tmp = av[src]; + int i; + for (i=src; i>dest; i--) + av[i] = av[i-1]; + av[dest] = tmp; +} + +int getopt_long(int argc, char *const *argv, const char *optstring, const struct option *longopts, int *idx) +{ + optarg = 0; + if (longopts && argv[optind][0] == '-' && + (argv[optind][1] == '-' && argv[optind][2])) + { + int colon = optstring[optstring[0]=='+'||optstring[0]=='-']==':'; + int i, cnt, match; + char *arg, *opt, *start = argv[optind]+1; + for (cnt=i=0; longopts[i].name; i++) { + const char *name = longopts[i].name; + opt = start; + if (*opt == '-') opt++; + while (*opt && *opt != '=' && *opt == *name) + name++, opt++; + if (*opt && *opt != '=') continue; + arg = opt; + match = i; + if (!*name) { + cnt = 1; + break; + } + cnt++; + } + if (cnt==1) { + i = match; + opt = arg; + optind++; + if (*opt == '=') { + if (!longopts[i].has_arg) { + optopt = longopts[i].val; + if (colon || !opterr) + return '?'; + __getopt_msg(argv[0], + ": option does not take an argument: ", + longopts[i].name, + strlen(longopts[i].name)); + return '?'; + } + optarg = opt+1; + } else if (longopts[i].has_arg == required_argument) { + if (!(optarg = argv[optind])) { + optopt = longopts[i].val; + if (colon) return ':'; + if (!opterr) return '?'; + __getopt_msg(argv[0], + ": option requires an argument: ", + longopts[i].name, + strlen(longopts[i].name)); + return '?'; + } + optind++; + } + if (idx) *idx = i; + if (longopts[i].flag) { + *longopts[i].flag = longopts[i].val; + return 0; + } + return longopts[i].val; + } + if (argv[optind][1] == '-') { + optopt = 0; + if (!colon && opterr) + __getopt_msg(argv[0], cnt ? + ": option is ambiguous: " : + ": unrecognized option: ", + argv[optind]+2, + strlen(argv[optind]+2)); + optind++; + return '?'; + } + } + return getopt(argc, argv, optstring); +} +#endif diff --git a/lib/compat/src/getsubopt.c b/lib/compat/src/getsubopt.c new file mode 100644 index 0000000..e6fea1a --- /dev/null +++ b/lib/compat/src/getsubopt.c @@ -0,0 +1,27 @@ +#include "compat.h" + +#include +#include + +#ifndef HAVE_GETSUBOPT +int getsubopt(char **opt, char *const *keys, char **val) +{ + char *s = *opt; + int i; + + *val = NULL; + *opt = strchr(s, ','); + if (*opt) *(*opt)++ = 0; + else *opt = s + strlen(s); + + for (i=0; keys[i]; i++) { + size_t l = strlen(keys[i]); + if (strncmp(keys[i], s, l)) continue; + if (s[l] == '=') + *val = s + l + 1; + else if (s[l]) continue; + return i; + } + return -1; +} +#endif diff --git a/lib/compat/src/mockups.c b/lib/compat/src/mockups.c new file mode 100644 index 0000000..4c396ec --- /dev/null +++ b/lib/compat/src/mockups.c @@ -0,0 +1,51 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * mockups.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "config.h" +#include "compat.h" + +#include + +#ifdef _WIN32 +int fchownat(int dirfd, const char *path, int uid, int gid, int flags) +{ + if (dirfd != AT_FDCWD) { + fputs("[FIXME] fchownat stub only supports AT_FDCWD!\n", + stderr); + return -1; + } + + if (flags != 0 && flags != AT_SYMLINK_NOFOLLOW) { + fputs("[FIXME] fchownat stub used with an unknown flag!\n", + stderr); + return -1; + } + + (void)path; + (void)uid; + (void)gid; + return 0; +} + +int fchmodat(int dirfd, const char *path, int mode, int flags) +{ + if (dirfd != AT_FDCWD) { + fputs("[FIXME] fchmodat stub only supports AT_FDCWD!\n", + stderr); + return -1; + } + + if (flags != 0 && flags != AT_SYMLINK_NOFOLLOW) { + fputs("[FIXME] fchmodat stub used with an unknown flag!\n", + stderr); + return -1; + } + + (void)path; + (void)mode; + return 0; +} +#endif diff --git a/lib/compat/src/path_to_windows.c b/lib/compat/src/path_to_windows.c new file mode 100644 index 0000000..ff3a5d2 --- /dev/null +++ b/lib/compat/src/path_to_windows.c @@ -0,0 +1,43 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * path_to_windows.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "config.h" +#include "compat.h" + +#include +#include + +#if defined(_WIN32) || defined(__WINDOWS__) +WCHAR *path_to_windows(const char *input) +{ + WCHAR *wpath, *ptr; + DWORD length; + + length = MultiByteToWideChar(CP_UTF8, 0, input, -1, NULL, 0); + if (length <= 0) { + fprintf(stderr, "Converting UTF-8 path to UTF-16: %ld\n", + GetLastError()); + return NULL; + } + + wpath = calloc(sizeof(wpath[0]), length + 1); + if (wpath == NULL) { + fprintf(stderr, + "Converting UTF-8 path to UTF-16: out of memory\n"); + return NULL; + } + + MultiByteToWideChar(CP_UTF8, 0, input, -1, wpath, length + 1); + wpath[length] = '\0'; + + for (ptr = wpath; *ptr != '\0'; ++ptr) { + if (*ptr == '/') + *ptr = '\\'; + } + + return wpath; +} +#endif diff --git a/lib/compat/src/strchrnul.c b/lib/compat/src/strchrnul.c new file mode 100644 index 0000000..7296060 --- /dev/null +++ b/lib/compat/src/strchrnul.c @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * strchrnul.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "config.h" +#include "compat.h" + +#ifndef HAVE_STRCHRNUL +char *strchrnul(const char *s, int c) +{ + while (*s && *((unsigned char *)s) != c) + ++s; + + return (char *)s; +} +#endif diff --git a/lib/compat/src/strndup.c b/lib/compat/src/strndup.c new file mode 100644 index 0000000..7e77f6c --- /dev/null +++ b/lib/compat/src/strndup.c @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * strndup.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "config.h" +#include "compat.h" + +#include +#include + +#ifndef HAVE_STRNDUP +char *strndup(const char *str, size_t max_len) +{ + size_t len = 0; + char *out; + + while (len < max_len && str[len] != '\0') + ++len; + + out = malloc(len + 1); + + if (out != NULL) { + memcpy(out, str, len); + out[len] = '\0'; + } + + return out; +} +#endif diff --git a/lib/compat/src/w32_perror.c b/lib/compat/src/w32_perror.c new file mode 100644 index 0000000..8c84191 --- /dev/null +++ b/lib/compat/src/w32_perror.c @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * w32_perror.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "config.h" +#include "compat.h" + +#include + +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include + +void w32_perror(const char *str) +{ + DWORD nStatus = GetLastError(); + LPVOID msg = NULL; + + FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, nStatus, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR)&msg, 0, NULL); + + fprintf(stderr, "%s: %s\n", str, (const char *)msg); + + if (msg != NULL) + LocalFree(msg); +} +#endif diff --git a/lib/compat/src/w32_stdio.c b/lib/compat/src/w32_stdio.c new file mode 100644 index 0000000..c7f68d0 --- /dev/null +++ b/lib/compat/src/w32_stdio.c @@ -0,0 +1,125 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * w32_stdio.c + * + * Copyright (C) 2021 David Oberhollenzer + */ +#include "config.h" +#include "compat.h" + +#include +#include + +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#include + +#undef fputc +#undef putc +#undef fputs +#undef fprintf +#undef printf + +static HANDLE get_handle(FILE *strm) +{ + if (strm != stdout && strm != stderr) + return INVALID_HANDLE_VALUE; + + return GetStdHandle(strm == stderr ? + STD_ERROR_HANDLE : STD_OUTPUT_HANDLE); +} + +static BOOL isatty(HANDLE hnd) +{ + if (hnd == INVALID_HANDLE_VALUE) + return FALSE; + + return (GetFileType(hnd) == FILE_TYPE_CHAR); +} + +int sqfs_tools_fputs(const char *str, FILE *strm) +{ + DWORD length; + WCHAR *wstr; + HANDLE hnd; + int ret; + + hnd = get_handle(strm); + if (!isatty(hnd)) + return fputs(str, strm); + + length = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0); + if (length <= 0) + return EOF; + + wstr = calloc(sizeof(wstr[0]), length); + if (wstr == NULL) + return EOF; + + MultiByteToWideChar(CP_UTF8, 0, str, -1, wstr, length); + + ret = WriteConsoleW(hnd, wstr, length, NULL, NULL) ? length : EOF; + + free(wstr); + return ret; +} + +int sqfs_tools_fputc(int c, FILE *strm) +{ + char str[2]; + + str[0] = c; + str[1] = '\0'; + + return sqfs_tools_fputs(str, strm); +} + +static int sqfs_printf_common(FILE *out, const char *fmt, va_list ap) +{ + int ret, len; + char *str; + + len = _vscprintf(fmt, ap); + if (len == -1) + return -1; + + str = malloc((size_t)len + 1); + if (str == NULL) + return -1; + + ret = vsprintf(str, fmt, ap); + if (ret == -1) { + free(str); + return -1; + } + + if (sqfs_tools_fputs(str, out) == EOF) + ret = -1; + + free(str); + return ret; +} + +int sqfs_tools_printf(const char *fmt, ...) +{ + va_list ap; + int ret; + + va_start(ap, fmt); + ret = sqfs_printf_common(stdout, fmt, ap); + va_end(ap); + return ret; +} + +int sqfs_tools_fprintf(FILE *strm, const char *fmt, ...) +{ + va_list ap; + int ret; + + va_start(ap, fmt); + ret = sqfs_printf_common(strm, fmt, ap); + va_end(ap); + return ret; +} +#endif diff --git a/lib/compat/src/w32_wmain.c b/lib/compat/src/w32_wmain.c new file mode 100644 index 0000000..9b3e354 --- /dev/null +++ b/lib/compat/src/w32_wmain.c @@ -0,0 +1,83 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * w32_wmain.c + * + * Copyright (C) 2021 David Oberhollenzer + */ +#include "config.h" +#include "compat.h" + +#include +#include + +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#include + +#undef main + +int main(int argc, char **argv) +{ + WCHAR *cmdline, **argList; + int i, ret, utf8_argc; + char **utf8_argv; + (void)argc; + (void)argv; + + /* get the UTF-16 encoded command line arguments */ + cmdline = GetCommandLineW(); + argList = CommandLineToArgvW(cmdline, &utf8_argc); + if (argList == NULL) + goto fail_oom; + + /* convert to UTF-8 */ + utf8_argv = calloc(sizeof(utf8_argv[0]), utf8_argc); + if (utf8_argv == NULL) + goto fail_oom; + + for (i = 0; i < utf8_argc; ++i) { + DWORD length = WideCharToMultiByte(CP_UTF8, 0, argList[i], + -1, NULL, 0, NULL, NULL); + if (length <= 0) + goto fail_conv; + + utf8_argv[i] = calloc(1, length + 1); + if (utf8_argv[i] == NULL) + goto fail_oom; + + WideCharToMultiByte(CP_UTF8, 0, argList[i], -1, + utf8_argv[i], length + 1, NULL, NULL); + utf8_argv[i][length] = '\0'; + } + + LocalFree(argList); + argList = NULL; + + /* call the actual main function */ + ret = sqfs_tools_main(utf8_argc, utf8_argv); + + /* cleanup */ + for (i = 0; i < utf8_argc; ++i) + free(utf8_argv[i]); + + free(utf8_argv); + return ret; +fail_conv: + w32_perror("Converting UTF-16 argument to UTF-8"); + goto fail; +fail_oom: + fputs("out of memory\n", stderr); + goto fail; +fail: + if (utf8_argv != NULL) { + for (i = 0; i < utf8_argc; ++i) + free(utf8_argv[i]); + free(utf8_argv); + } + if (argList != NULL) { + LocalFree(argList); + } + return EXIT_FAILURE; +} +#endif diff --git a/lib/compat/strchrnul.c b/lib/compat/strchrnul.c deleted file mode 100644 index 7296060..0000000 --- a/lib/compat/strchrnul.c +++ /dev/null @@ -1,18 +0,0 @@ -/* SPDX-License-Identifier: LGPL-3.0-or-later */ -/* - * strchrnul.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "config.h" -#include "compat.h" - -#ifndef HAVE_STRCHRNUL -char *strchrnul(const char *s, int c) -{ - while (*s && *((unsigned char *)s) != c) - ++s; - - return (char *)s; -} -#endif diff --git a/lib/compat/strndup.c b/lib/compat/strndup.c deleted file mode 100644 index 7e77f6c..0000000 --- a/lib/compat/strndup.c +++ /dev/null @@ -1,31 +0,0 @@ -/* SPDX-License-Identifier: LGPL-3.0-or-later */ -/* - * strndup.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "config.h" -#include "compat.h" - -#include -#include - -#ifndef HAVE_STRNDUP -char *strndup(const char *str, size_t max_len) -{ - size_t len = 0; - char *out; - - while (len < max_len && str[len] != '\0') - ++len; - - out = malloc(len + 1); - - if (out != NULL) { - memcpy(out, str, len); - out[len] = '\0'; - } - - return out; -} -#endif diff --git a/lib/compat/w32_perror.c b/lib/compat/w32_perror.c deleted file mode 100644 index 8c84191..0000000 --- a/lib/compat/w32_perror.c +++ /dev/null @@ -1,33 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * w32_perror.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "config.h" -#include "compat.h" - -#include - -#ifdef _WIN32 -#define WIN32_LEAN_AND_MEAN -#include - -void w32_perror(const char *str) -{ - DWORD nStatus = GetLastError(); - LPVOID msg = NULL; - - FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | - FORMAT_MESSAGE_FROM_SYSTEM | - FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, nStatus, - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - (LPTSTR)&msg, 0, NULL); - - fprintf(stderr, "%s: %s\n", str, (const char *)msg); - - if (msg != NULL) - LocalFree(msg); -} -#endif diff --git a/lib/compat/w32_stdio.c b/lib/compat/w32_stdio.c deleted file mode 100644 index c7f68d0..0000000 --- a/lib/compat/w32_stdio.c +++ /dev/null @@ -1,125 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * w32_stdio.c - * - * Copyright (C) 2021 David Oberhollenzer - */ -#include "config.h" -#include "compat.h" - -#include -#include - -#ifdef _WIN32 -#define WIN32_LEAN_AND_MEAN -#include -#include - -#undef fputc -#undef putc -#undef fputs -#undef fprintf -#undef printf - -static HANDLE get_handle(FILE *strm) -{ - if (strm != stdout && strm != stderr) - return INVALID_HANDLE_VALUE; - - return GetStdHandle(strm == stderr ? - STD_ERROR_HANDLE : STD_OUTPUT_HANDLE); -} - -static BOOL isatty(HANDLE hnd) -{ - if (hnd == INVALID_HANDLE_VALUE) - return FALSE; - - return (GetFileType(hnd) == FILE_TYPE_CHAR); -} - -int sqfs_tools_fputs(const char *str, FILE *strm) -{ - DWORD length; - WCHAR *wstr; - HANDLE hnd; - int ret; - - hnd = get_handle(strm); - if (!isatty(hnd)) - return fputs(str, strm); - - length = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0); - if (length <= 0) - return EOF; - - wstr = calloc(sizeof(wstr[0]), length); - if (wstr == NULL) - return EOF; - - MultiByteToWideChar(CP_UTF8, 0, str, -1, wstr, length); - - ret = WriteConsoleW(hnd, wstr, length, NULL, NULL) ? length : EOF; - - free(wstr); - return ret; -} - -int sqfs_tools_fputc(int c, FILE *strm) -{ - char str[2]; - - str[0] = c; - str[1] = '\0'; - - return sqfs_tools_fputs(str, strm); -} - -static int sqfs_printf_common(FILE *out, const char *fmt, va_list ap) -{ - int ret, len; - char *str; - - len = _vscprintf(fmt, ap); - if (len == -1) - return -1; - - str = malloc((size_t)len + 1); - if (str == NULL) - return -1; - - ret = vsprintf(str, fmt, ap); - if (ret == -1) { - free(str); - return -1; - } - - if (sqfs_tools_fputs(str, out) == EOF) - ret = -1; - - free(str); - return ret; -} - -int sqfs_tools_printf(const char *fmt, ...) -{ - va_list ap; - int ret; - - va_start(ap, fmt); - ret = sqfs_printf_common(stdout, fmt, ap); - va_end(ap); - return ret; -} - -int sqfs_tools_fprintf(FILE *strm, const char *fmt, ...) -{ - va_list ap; - int ret; - - va_start(ap, fmt); - ret = sqfs_printf_common(strm, fmt, ap); - va_end(ap); - return ret; -} -#endif diff --git a/lib/compat/w32_wmain.c b/lib/compat/w32_wmain.c deleted file mode 100644 index 9b3e354..0000000 --- a/lib/compat/w32_wmain.c +++ /dev/null @@ -1,83 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * w32_wmain.c - * - * Copyright (C) 2021 David Oberhollenzer - */ -#include "config.h" -#include "compat.h" - -#include -#include - -#ifdef _WIN32 -#define WIN32_LEAN_AND_MEAN -#include -#include - -#undef main - -int main(int argc, char **argv) -{ - WCHAR *cmdline, **argList; - int i, ret, utf8_argc; - char **utf8_argv; - (void)argc; - (void)argv; - - /* get the UTF-16 encoded command line arguments */ - cmdline = GetCommandLineW(); - argList = CommandLineToArgvW(cmdline, &utf8_argc); - if (argList == NULL) - goto fail_oom; - - /* convert to UTF-8 */ - utf8_argv = calloc(sizeof(utf8_argv[0]), utf8_argc); - if (utf8_argv == NULL) - goto fail_oom; - - for (i = 0; i < utf8_argc; ++i) { - DWORD length = WideCharToMultiByte(CP_UTF8, 0, argList[i], - -1, NULL, 0, NULL, NULL); - if (length <= 0) - goto fail_conv; - - utf8_argv[i] = calloc(1, length + 1); - if (utf8_argv[i] == NULL) - goto fail_oom; - - WideCharToMultiByte(CP_UTF8, 0, argList[i], -1, - utf8_argv[i], length + 1, NULL, NULL); - utf8_argv[i][length] = '\0'; - } - - LocalFree(argList); - argList = NULL; - - /* call the actual main function */ - ret = sqfs_tools_main(utf8_argc, utf8_argv); - - /* cleanup */ - for (i = 0; i < utf8_argc; ++i) - free(utf8_argv[i]); - - free(utf8_argv); - return ret; -fail_conv: - w32_perror("Converting UTF-16 argument to UTF-8"); - goto fail; -fail_oom: - fputs("out of memory\n", stderr); - goto fail; -fail: - if (utf8_argv != NULL) { - for (i = 0; i < utf8_argc; ++i) - free(utf8_argv[i]); - free(utf8_argv); - } - if (argList != NULL) { - LocalFree(argList); - } - return EXIT_FAILURE; -} -#endif diff --git a/lib/fstree/Makemodule.am b/lib/fstree/Makemodule.am index 09cd9ac..f740527 100644 --- a/lib/fstree/Makemodule.am +++ b/lib/fstree/Makemodule.am @@ -1,8 +1,6 @@ -libfstree_a_SOURCES = include/fstree.h lib/fstree/fstree.c -libfstree_a_SOURCES += lib/fstree/post_process.c lib/fstree/get_path.c -libfstree_a_SOURCES += lib/fstree/mknode.c lib/fstree/hardlink.c -libfstree_a_SOURCES += lib/fstree/add_by_path.c lib/fstree/get_by_path.c -libfstree_a_CFLAGS = $(AM_CFLAGS) -libfstree_a_CPPFLAGS = $(AM_CPPFLAGS) +libfstree_a_SOURCES = include/fstree.h lib/fstree/src/fstree.c \ + lib/fstree/src/post_process.c lib/fstree/src/get_path.c \ + lib/fstree/src/mknode.c lib/fstree/src/hardlink.c \ + lib/fstree/src/add_by_path.c lib/fstree/src/get_by_path.c noinst_LIBRARIES += libfstree.a diff --git a/lib/fstree/add_by_path.c b/lib/fstree/add_by_path.c deleted file mode 100644 index 0afd898..0000000 --- a/lib/fstree/add_by_path.c +++ /dev/null @@ -1,54 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * add_by_path.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "config.h" - -#include "fstree.h" - -#include -#include -#include - -tree_node_t *fstree_add_generic(fstree_t *fs, const char *path, - const struct stat *sb, const char *extra) -{ - tree_node_t *child, *parent; - const char *name; - - if (*path == '\0') { - child = fs->root; - assert(child != NULL); - goto out; - } - - parent = fstree_get_node_by_path(fs, fs->root, path, true, true); - if (parent == NULL) - return NULL; - - name = strrchr(path, '/'); - name = (name == NULL ? path : (name + 1)); - - child = parent->data.dir.children; - while (child != NULL && strcmp(child->name, name) != 0) - child = child->next; -out: - if (child != NULL) { - if (!S_ISDIR(child->mode) || !S_ISDIR(sb->st_mode) || - !child->data.dir.created_implicitly) { - errno = EEXIST; - return NULL; - } - - child->uid = sb->st_uid; - child->gid = sb->st_gid; - child->mode = sb->st_mode; - child->mod_time = sb->st_mtime; - child->data.dir.created_implicitly = false; - return child; - } - - return fstree_mknode(parent, name, strlen(name), extra, sb); -} diff --git a/lib/fstree/fstree.c b/lib/fstree/fstree.c deleted file mode 100644 index d44a8ae..0000000 --- a/lib/fstree/fstree.c +++ /dev/null @@ -1,135 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * fstree.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "config.h" -#include "fstree.h" - -#include "util/util.h" - -#include -#include -#include - -enum { - DEF_UID = 0, - DEF_GID, - DEF_MODE, - DEF_MTIME, -}; - -static const char *defaults[] = { - [DEF_UID] = "uid", - [DEF_GID] = "gid", - [DEF_MODE] = "mode", - [DEF_MTIME] = "mtime", - NULL -}; - -static int process_defaults(struct stat *sb, char *subopts) -{ - char *value; - long lval; - int i; - - while (*subopts != '\0') { - i = getsubopt(&subopts, (char *const *)defaults, &value); - - if (value == NULL) { - fprintf(stderr, "Missing value for option %s\n", - defaults[i]); - return -1; - } - - switch (i) { - case DEF_UID: - lval = strtol(value, NULL, 0); - if (lval < 0) - goto fail_uv; - if (lval > (long)INT32_MAX) - goto fail_ov; - sb->st_uid = lval; - break; - case DEF_GID: - lval = strtol(value, NULL, 0); - if (lval < 0) - goto fail_uv; - if (lval > (long)INT32_MAX) - goto fail_ov; - sb->st_gid = lval; - break; - case DEF_MODE: - lval = strtol(value, NULL, 0); - if (lval < 0) - goto fail_uv; - if (lval > 07777) - goto fail_ov; - sb->st_mode = S_IFDIR | (sqfs_u16)lval; - break; - case DEF_MTIME: - lval = strtol(value, NULL, 0); - if (lval < 0) - goto fail_uv; - if (lval > (long)INT32_MAX) - goto fail_ov; - sb->st_mtime = lval; - break; - default: - fprintf(stderr, "Unknown option '%s'\n", value); - return -1; - } - } - return 0; -fail_uv: - fprintf(stderr, "%s: value must be positive\n", defaults[i]); - return -1; -fail_ov: - fprintf(stderr, "%s: value too large\n", defaults[i]); - return -1; -} - -static void free_recursive(tree_node_t *n) -{ - tree_node_t *it; - - if (S_ISDIR(n->mode)) { - while (n->data.dir.children != NULL) { - it = n->data.dir.children; - n->data.dir.children = it->next; - - free_recursive(it); - } - } - - free(n); -} - -int fstree_init(fstree_t *fs, char *defaults) -{ - memset(fs, 0, sizeof(*fs)); - fs->defaults.st_mode = S_IFDIR | 0755; - fs->defaults.st_blksize = 512; - fs->defaults.st_mtime = get_source_date_epoch(); - - if (defaults != NULL && process_defaults(&fs->defaults, defaults) != 0) - return -1; - - fs->root = fstree_mknode(NULL, "", 0, NULL, &fs->defaults); - - if (fs->root == NULL) { - perror("initializing file system tree"); - return -1; - } - - fs->root->data.dir.created_implicitly = true; - return 0; -} - -void fstree_cleanup(fstree_t *fs) -{ - free_recursive(fs->root); - free(fs->inodes); - memset(fs, 0, sizeof(*fs)); -} diff --git a/lib/fstree/get_by_path.c b/lib/fstree/get_by_path.c deleted file mode 100644 index 8742892..0000000 --- a/lib/fstree/get_by_path.c +++ /dev/null @@ -1,76 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * get_by_path.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "config.h" - -#include "fstree.h" - -#include -#include - -static tree_node_t *child_by_name(tree_node_t *root, const char *name, - size_t len) -{ - tree_node_t *n = root->data.dir.children; - - while (n != NULL) { - if (strncmp(n->name, name, len) == 0 && n->name[len] == '\0') - break; - - n = n->next; - } - - return n; -} - -tree_node_t *fstree_get_node_by_path(fstree_t *fs, tree_node_t *root, - const char *path, bool create_implicitly, - bool stop_at_parent) -{ - const char *end; - tree_node_t *n; - size_t len; - - while (*path != '\0') { - while (*path == '/') - ++path; - - if (!S_ISDIR(root->mode)) { - errno = ENOTDIR; - return NULL; - } - - end = strchr(path, '/'); - if (end == NULL) { - if (stop_at_parent) - break; - - len = strlen(path); - } else { - len = end - path; - } - - n = child_by_name(root, path, len); - - if (n == NULL) { - if (!create_implicitly) { - errno = ENOENT; - return NULL; - } - - n = fstree_mknode(root, path, len, NULL, &fs->defaults); - if (n == NULL) - return NULL; - - n->data.dir.created_implicitly = true; - } - - root = n; - path = path + len; - } - - return root; -} diff --git a/lib/fstree/get_path.c b/lib/fstree/get_path.c deleted file mode 100644 index decf92e..0000000 --- a/lib/fstree/get_path.c +++ /dev/null @@ -1,43 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * get_path.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "config.h" - -#include "fstree.h" - -#include -#include - -char *fstree_get_path(tree_node_t *node) -{ - tree_node_t *it; - char *str, *ptr; - size_t len = 0; - - if (node->parent == NULL) - return strdup("/"); - - for (it = node; it != NULL && it->parent != NULL; it = it->parent) { - len += strlen(it->name) + 1; - } - - str = malloc(len + 1); - if (str == NULL) - return NULL; - - ptr = str + len; - *ptr = '\0'; - - for (it = node; it != NULL && it->parent != NULL; it = it->parent) { - len = strlen(it->name); - ptr -= len; - - memcpy(ptr, it->name, len); - *(--ptr) = '/'; - } - - return str; -} diff --git a/lib/fstree/hardlink.c b/lib/fstree/hardlink.c deleted file mode 100644 index 2165b5f..0000000 --- a/lib/fstree/hardlink.c +++ /dev/null @@ -1,76 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * hardlink.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "config.h" - -#include "util/util.h" -#include "fstree.h" - -#include -#include -#include - -tree_node_t *fstree_add_hard_link(fstree_t *fs, const char *path, - const char *target) -{ - struct stat sb; - tree_node_t *n; - - memset(&sb, 0, sizeof(sb)); - sb.st_mode = S_IFLNK | 0777; - - n = fstree_add_generic(fs, path, &sb, target); - if (n != NULL) { - if (canonicalize_name(n->data.target)) { - free(n); - errno = EINVAL; - return NULL; - } - - n->mode = FSTREE_MODE_HARD_LINK; - } - - return n; -} - -int fstree_resolve_hard_link(fstree_t *fs, tree_node_t *node) -{ - tree_node_t *start = node; - - while (node->mode == FSTREE_MODE_HARD_LINK || - node->mode == FSTREE_MODE_HARD_LINK_RESOLVED) { - if (node->mode == FSTREE_MODE_HARD_LINK_RESOLVED) { - node = node->data.target_node; - } else { - node = fstree_get_node_by_path(fs, fs->root, - node->data.target, - false, false); - if (node == NULL) - return -1; - } - - if (node == start) { - errno = EMLINK; - return -1; - } - } - - if (S_ISDIR(node->mode)) { - errno = EPERM; - return -1; - } - - if (node->link_count == 0xFFFFFFFF) { - errno = EMLINK; - return -1; - } - - start->mode = FSTREE_MODE_HARD_LINK_RESOLVED; - start->data.target_node = node; - - node->link_count++; - return 0; -} diff --git a/lib/fstree/mknode.c b/lib/fstree/mknode.c deleted file mode 100644 index 2faf901..0000000 --- a/lib/fstree/mknode.c +++ /dev/null @@ -1,101 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * mknode.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "config.h" -#include "fstree.h" - -#include -#include -#include - -void fstree_insert_sorted(tree_node_t *root, tree_node_t *n) -{ - tree_node_t *it = root->data.dir.children, *prev = NULL; - - while (it != NULL && strcmp(it->name, n->name) < 0) { - prev = it; - it = it->next; - } - - n->parent = root; - n->next = it; - - if (prev == NULL) { - root->data.dir.children = n; - } else { - prev->next = n; - } -} - -tree_node_t *fstree_mknode(tree_node_t *parent, const char *name, - size_t name_len, const char *extra, - const struct stat *sb) -{ - tree_node_t *n; - size_t size; - char *ptr; - - if (S_ISLNK(sb->st_mode) && extra == NULL) { - errno = EINVAL; - return NULL; - } - - size = sizeof(tree_node_t) + name_len + 1; - if (extra != NULL) - size += strlen(extra) + 1; - - n = calloc(1, size); - if (n == NULL) - return NULL; - - n->xattr_idx = 0xFFFFFFFF; - n->uid = sb->st_uid; - n->gid = sb->st_gid; - n->mode = sb->st_mode; - n->mod_time = sb->st_mtime; - n->link_count = 1; - n->name = (char *)n->payload; - memcpy(n->name, name, name_len); - - if (extra != NULL) { - ptr = n->name + name_len + 1; - strcpy(ptr, extra); - } else { - ptr = NULL; - } - - switch (sb->st_mode & S_IFMT) { - case S_IFREG: - n->data.file.input_file = ptr; - break; - case S_IFLNK: - n->mode = S_IFLNK | 0777; - n->data.target = ptr; - break; - case S_IFBLK: - case S_IFCHR: - n->data.devno = sb->st_rdev; - break; - case S_IFDIR: - n->link_count = 2; - break; - default: - break; - } - - if (parent != NULL) { - if (parent->link_count == 0xFFFFFFFF) { - free(n); - errno = EMLINK; - return NULL; - } - - fstree_insert_sorted(parent, n); - parent->link_count++; - } - - return n; -} diff --git a/lib/fstree/post_process.c b/lib/fstree/post_process.c deleted file mode 100644 index 088916b..0000000 --- a/lib/fstree/post_process.c +++ /dev/null @@ -1,221 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * post_process.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "config.h" -#include "fstree.h" - -#include -#include -#include -#include -#include - -static int alloc_inode_num_dfs(fstree_t *fs, tree_node_t *root) -{ - bool has_subdirs = false; - tree_node_t *it; - size_t inum; - - for (it = root->data.dir.children; it != NULL; it = it->next) { - if (S_ISDIR(it->mode)) { - has_subdirs = true; - break; - } - } - - if (has_subdirs) { - for (it = root->data.dir.children; it != NULL; it = it->next) { - if (S_ISDIR(it->mode)) { - if (alloc_inode_num_dfs(fs, it)) - return -1; - } - } - } - - for (it = root->data.dir.children; it != NULL; it = it->next) { - if (it->mode != FSTREE_MODE_HARD_LINK_RESOLVED) { - if (SZ_ADD_OV(fs->unique_inode_count, 1, &inum)) - goto fail_ov; - - if ((sizeof(size_t) > sizeof(sqfs_u32)) && - inum > 0x0FFFFFFFFUL) { - goto fail_ov; - } - - it->inode_num = (sqfs_u32)inum; - fs->unique_inode_count = inum; - } - } - - return 0; -fail_ov: - fputs("Too many inodes.\n", stderr); - return -1; -} - -static int resolve_hard_links_dfs(fstree_t *fs, tree_node_t *n) -{ - tree_node_t *it; - - if (n->mode == FSTREE_MODE_HARD_LINK) { - if (fstree_resolve_hard_link(fs, n)) - goto fail_link; - - assert(n->mode == FSTREE_MODE_HARD_LINK_RESOLVED); - it = n->data.target_node; - - if (S_ISDIR(it->mode) && it->data.dir.visited) - goto fail_link_loop; - } else if (S_ISDIR(n->mode)) { - n->data.dir.visited = true; - - for (it = n->data.dir.children; it != NULL; it = it->next) { - if (resolve_hard_links_dfs(fs, it)) - return -1; - } - - n->data.dir.visited = false; - } - - return 0; -fail_link: { - char *path = fstree_get_path(n); - fprintf(stderr, "Resolving hard link '%s' -> '%s': %s\n", - path == NULL ? n->name : path, n->data.target, - strerror(errno)); - free(path); -} - return -1; -fail_link_loop: { - char *npath = fstree_get_path(n); - char *tpath = fstree_get_path(it); - fprintf(stderr, "Hard link loop detected in '%s' -> '%s'\n", - npath == NULL ? n->name : npath, - tpath == NULL ? it->name : tpath); - free(npath); - free(tpath); -} - return -1; -} - -static file_info_t *file_list_dfs(tree_node_t *n) -{ - if (S_ISREG(n->mode)) { - n->data.file.next = NULL; - return &n->data.file; - } - - if (S_ISDIR(n->mode)) { - file_info_t *list = NULL, *last = NULL; - - for (n = n->data.dir.children; n != NULL; n = n->next) { - if (list == NULL) { - list = file_list_dfs(n); - if (list == NULL) - continue; - last = list; - } else { - last->next = file_list_dfs(n); - } - - while (last->next != NULL) - last = last->next; - } - - return list; - } - - return NULL; -} - -static void map_inodes_dfs(fstree_t *fs, tree_node_t *n) -{ - if (n->mode == FSTREE_MODE_HARD_LINK_RESOLVED) - return; - - fs->inodes[n->inode_num - 1] = n; - - if (S_ISDIR(n->mode)) { - for (n = n->data.dir.children; n != NULL; n = n->next) - map_inodes_dfs(fs, n); - } -} - -static void reorder_hard_links(fstree_t *fs) -{ - size_t i, j, tgt_idx; - tree_node_t *it, *tgt; - - for (i = 0; i < fs->unique_inode_count; ++i) { - if (!S_ISDIR(fs->inodes[i]->mode)) - continue; - - it = fs->inodes[i]->data.dir.children; - - for (; it != NULL; it = it->next) { - if (it->mode != FSTREE_MODE_HARD_LINK_RESOLVED) - continue; - - tgt = it->data.target_node; - tgt_idx = tgt->inode_num - 1; - - if (tgt_idx <= i) - continue; - - /* TODO ? */ - assert(!S_ISDIR(tgt->mode)); - - for (j = tgt_idx; j > i; --j) { - fs->inodes[j] = fs->inodes[j - 1]; - fs->inodes[j]->inode_num += 1; - } - - fs->inodes[i] = tgt; - - /* XXX: the possible overflow is checked for - during allocation */ - tgt->inode_num = (sqfs_u32)(i + 1); - ++i; - } - } -} - -int fstree_post_process(fstree_t *fs) -{ - size_t inum; - - if (resolve_hard_links_dfs(fs, fs->root)) - return -1; - - fs->unique_inode_count = 0; - - if (alloc_inode_num_dfs(fs, fs->root)) - return -1; - - if (SZ_ADD_OV(fs->unique_inode_count, 1, &inum)) - goto fail_root_ov; - - if ((sizeof(size_t) > sizeof(sqfs_u32)) && inum > 0x0FFFFFFFFUL) - goto fail_root_ov; - - fs->root->inode_num = (sqfs_u32)inum; - fs->unique_inode_count = inum; - - fs->inodes = calloc(sizeof(fs->inodes[0]), fs->unique_inode_count); - if (fs->inodes == NULL) { - perror("Allocating inode list"); - return -1; - } - - map_inodes_dfs(fs, fs->root); - reorder_hard_links(fs); - - fs->files = file_list_dfs(fs->root); - return 0; -fail_root_ov: - fputs("Too many inodes, cannot allocate number for root.\n", stderr); - return -1; -} diff --git a/lib/fstree/src/add_by_path.c b/lib/fstree/src/add_by_path.c new file mode 100644 index 0000000..0afd898 --- /dev/null +++ b/lib/fstree/src/add_by_path.c @@ -0,0 +1,54 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * add_by_path.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "config.h" + +#include "fstree.h" + +#include +#include +#include + +tree_node_t *fstree_add_generic(fstree_t *fs, const char *path, + const struct stat *sb, const char *extra) +{ + tree_node_t *child, *parent; + const char *name; + + if (*path == '\0') { + child = fs->root; + assert(child != NULL); + goto out; + } + + parent = fstree_get_node_by_path(fs, fs->root, path, true, true); + if (parent == NULL) + return NULL; + + name = strrchr(path, '/'); + name = (name == NULL ? path : (name + 1)); + + child = parent->data.dir.children; + while (child != NULL && strcmp(child->name, name) != 0) + child = child->next; +out: + if (child != NULL) { + if (!S_ISDIR(child->mode) || !S_ISDIR(sb->st_mode) || + !child->data.dir.created_implicitly) { + errno = EEXIST; + return NULL; + } + + child->uid = sb->st_uid; + child->gid = sb->st_gid; + child->mode = sb->st_mode; + child->mod_time = sb->st_mtime; + child->data.dir.created_implicitly = false; + return child; + } + + return fstree_mknode(parent, name, strlen(name), extra, sb); +} diff --git a/lib/fstree/src/fstree.c b/lib/fstree/src/fstree.c new file mode 100644 index 0000000..d44a8ae --- /dev/null +++ b/lib/fstree/src/fstree.c @@ -0,0 +1,135 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * fstree.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "config.h" +#include "fstree.h" + +#include "util/util.h" + +#include +#include +#include + +enum { + DEF_UID = 0, + DEF_GID, + DEF_MODE, + DEF_MTIME, +}; + +static const char *defaults[] = { + [DEF_UID] = "uid", + [DEF_GID] = "gid", + [DEF_MODE] = "mode", + [DEF_MTIME] = "mtime", + NULL +}; + +static int process_defaults(struct stat *sb, char *subopts) +{ + char *value; + long lval; + int i; + + while (*subopts != '\0') { + i = getsubopt(&subopts, (char *const *)defaults, &value); + + if (value == NULL) { + fprintf(stderr, "Missing value for option %s\n", + defaults[i]); + return -1; + } + + switch (i) { + case DEF_UID: + lval = strtol(value, NULL, 0); + if (lval < 0) + goto fail_uv; + if (lval > (long)INT32_MAX) + goto fail_ov; + sb->st_uid = lval; + break; + case DEF_GID: + lval = strtol(value, NULL, 0); + if (lval < 0) + goto fail_uv; + if (lval > (long)INT32_MAX) + goto fail_ov; + sb->st_gid = lval; + break; + case DEF_MODE: + lval = strtol(value, NULL, 0); + if (lval < 0) + goto fail_uv; + if (lval > 07777) + goto fail_ov; + sb->st_mode = S_IFDIR | (sqfs_u16)lval; + break; + case DEF_MTIME: + lval = strtol(value, NULL, 0); + if (lval < 0) + goto fail_uv; + if (lval > (long)INT32_MAX) + goto fail_ov; + sb->st_mtime = lval; + break; + default: + fprintf(stderr, "Unknown option '%s'\n", value); + return -1; + } + } + return 0; +fail_uv: + fprintf(stderr, "%s: value must be positive\n", defaults[i]); + return -1; +fail_ov: + fprintf(stderr, "%s: value too large\n", defaults[i]); + return -1; +} + +static void free_recursive(tree_node_t *n) +{ + tree_node_t *it; + + if (S_ISDIR(n->mode)) { + while (n->data.dir.children != NULL) { + it = n->data.dir.children; + n->data.dir.children = it->next; + + free_recursive(it); + } + } + + free(n); +} + +int fstree_init(fstree_t *fs, char *defaults) +{ + memset(fs, 0, sizeof(*fs)); + fs->defaults.st_mode = S_IFDIR | 0755; + fs->defaults.st_blksize = 512; + fs->defaults.st_mtime = get_source_date_epoch(); + + if (defaults != NULL && process_defaults(&fs->defaults, defaults) != 0) + return -1; + + fs->root = fstree_mknode(NULL, "", 0, NULL, &fs->defaults); + + if (fs->root == NULL) { + perror("initializing file system tree"); + return -1; + } + + fs->root->data.dir.created_implicitly = true; + return 0; +} + +void fstree_cleanup(fstree_t *fs) +{ + free_recursive(fs->root); + free(fs->inodes); + memset(fs, 0, sizeof(*fs)); +} diff --git a/lib/fstree/src/get_by_path.c b/lib/fstree/src/get_by_path.c new file mode 100644 index 0000000..8742892 --- /dev/null +++ b/lib/fstree/src/get_by_path.c @@ -0,0 +1,76 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * get_by_path.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "config.h" + +#include "fstree.h" + +#include +#include + +static tree_node_t *child_by_name(tree_node_t *root, const char *name, + size_t len) +{ + tree_node_t *n = root->data.dir.children; + + while (n != NULL) { + if (strncmp(n->name, name, len) == 0 && n->name[len] == '\0') + break; + + n = n->next; + } + + return n; +} + +tree_node_t *fstree_get_node_by_path(fstree_t *fs, tree_node_t *root, + const char *path, bool create_implicitly, + bool stop_at_parent) +{ + const char *end; + tree_node_t *n; + size_t len; + + while (*path != '\0') { + while (*path == '/') + ++path; + + if (!S_ISDIR(root->mode)) { + errno = ENOTDIR; + return NULL; + } + + end = strchr(path, '/'); + if (end == NULL) { + if (stop_at_parent) + break; + + len = strlen(path); + } else { + len = end - path; + } + + n = child_by_name(root, path, len); + + if (n == NULL) { + if (!create_implicitly) { + errno = ENOENT; + return NULL; + } + + n = fstree_mknode(root, path, len, NULL, &fs->defaults); + if (n == NULL) + return NULL; + + n->data.dir.created_implicitly = true; + } + + root = n; + path = path + len; + } + + return root; +} diff --git a/lib/fstree/src/get_path.c b/lib/fstree/src/get_path.c new file mode 100644 index 0000000..decf92e --- /dev/null +++ b/lib/fstree/src/get_path.c @@ -0,0 +1,43 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * get_path.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "config.h" + +#include "fstree.h" + +#include +#include + +char *fstree_get_path(tree_node_t *node) +{ + tree_node_t *it; + char *str, *ptr; + size_t len = 0; + + if (node->parent == NULL) + return strdup("/"); + + for (it = node; it != NULL && it->parent != NULL; it = it->parent) { + len += strlen(it->name) + 1; + } + + str = malloc(len + 1); + if (str == NULL) + return NULL; + + ptr = str + len; + *ptr = '\0'; + + for (it = node; it != NULL && it->parent != NULL; it = it->parent) { + len = strlen(it->name); + ptr -= len; + + memcpy(ptr, it->name, len); + *(--ptr) = '/'; + } + + return str; +} diff --git a/lib/fstree/src/hardlink.c b/lib/fstree/src/hardlink.c new file mode 100644 index 0000000..2165b5f --- /dev/null +++ b/lib/fstree/src/hardlink.c @@ -0,0 +1,76 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * hardlink.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "config.h" + +#include "util/util.h" +#include "fstree.h" + +#include +#include +#include + +tree_node_t *fstree_add_hard_link(fstree_t *fs, const char *path, + const char *target) +{ + struct stat sb; + tree_node_t *n; + + memset(&sb, 0, sizeof(sb)); + sb.st_mode = S_IFLNK | 0777; + + n = fstree_add_generic(fs, path, &sb, target); + if (n != NULL) { + if (canonicalize_name(n->data.target)) { + free(n); + errno = EINVAL; + return NULL; + } + + n->mode = FSTREE_MODE_HARD_LINK; + } + + return n; +} + +int fstree_resolve_hard_link(fstree_t *fs, tree_node_t *node) +{ + tree_node_t *start = node; + + while (node->mode == FSTREE_MODE_HARD_LINK || + node->mode == FSTREE_MODE_HARD_LINK_RESOLVED) { + if (node->mode == FSTREE_MODE_HARD_LINK_RESOLVED) { + node = node->data.target_node; + } else { + node = fstree_get_node_by_path(fs, fs->root, + node->data.target, + false, false); + if (node == NULL) + return -1; + } + + if (node == start) { + errno = EMLINK; + return -1; + } + } + + if (S_ISDIR(node->mode)) { + errno = EPERM; + return -1; + } + + if (node->link_count == 0xFFFFFFFF) { + errno = EMLINK; + return -1; + } + + start->mode = FSTREE_MODE_HARD_LINK_RESOLVED; + start->data.target_node = node; + + node->link_count++; + return 0; +} diff --git a/lib/fstree/src/mknode.c b/lib/fstree/src/mknode.c new file mode 100644 index 0000000..2faf901 --- /dev/null +++ b/lib/fstree/src/mknode.c @@ -0,0 +1,101 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * mknode.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "config.h" +#include "fstree.h" + +#include +#include +#include + +void fstree_insert_sorted(tree_node_t *root, tree_node_t *n) +{ + tree_node_t *it = root->data.dir.children, *prev = NULL; + + while (it != NULL && strcmp(it->name, n->name) < 0) { + prev = it; + it = it->next; + } + + n->parent = root; + n->next = it; + + if (prev == NULL) { + root->data.dir.children = n; + } else { + prev->next = n; + } +} + +tree_node_t *fstree_mknode(tree_node_t *parent, const char *name, + size_t name_len, const char *extra, + const struct stat *sb) +{ + tree_node_t *n; + size_t size; + char *ptr; + + if (S_ISLNK(sb->st_mode) && extra == NULL) { + errno = EINVAL; + return NULL; + } + + size = sizeof(tree_node_t) + name_len + 1; + if (extra != NULL) + size += strlen(extra) + 1; + + n = calloc(1, size); + if (n == NULL) + return NULL; + + n->xattr_idx = 0xFFFFFFFF; + n->uid = sb->st_uid; + n->gid = sb->st_gid; + n->mode = sb->st_mode; + n->mod_time = sb->st_mtime; + n->link_count = 1; + n->name = (char *)n->payload; + memcpy(n->name, name, name_len); + + if (extra != NULL) { + ptr = n->name + name_len + 1; + strcpy(ptr, extra); + } else { + ptr = NULL; + } + + switch (sb->st_mode & S_IFMT) { + case S_IFREG: + n->data.file.input_file = ptr; + break; + case S_IFLNK: + n->mode = S_IFLNK | 0777; + n->data.target = ptr; + break; + case S_IFBLK: + case S_IFCHR: + n->data.devno = sb->st_rdev; + break; + case S_IFDIR: + n->link_count = 2; + break; + default: + break; + } + + if (parent != NULL) { + if (parent->link_count == 0xFFFFFFFF) { + free(n); + errno = EMLINK; + return NULL; + } + + fstree_insert_sorted(parent, n); + parent->link_count++; + } + + return n; +} diff --git a/lib/fstree/src/post_process.c b/lib/fstree/src/post_process.c new file mode 100644 index 0000000..088916b --- /dev/null +++ b/lib/fstree/src/post_process.c @@ -0,0 +1,221 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * post_process.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "config.h" +#include "fstree.h" + +#include +#include +#include +#include +#include + +static int alloc_inode_num_dfs(fstree_t *fs, tree_node_t *root) +{ + bool has_subdirs = false; + tree_node_t *it; + size_t inum; + + for (it = root->data.dir.children; it != NULL; it = it->next) { + if (S_ISDIR(it->mode)) { + has_subdirs = true; + break; + } + } + + if (has_subdirs) { + for (it = root->data.dir.children; it != NULL; it = it->next) { + if (S_ISDIR(it->mode)) { + if (alloc_inode_num_dfs(fs, it)) + return -1; + } + } + } + + for (it = root->data.dir.children; it != NULL; it = it->next) { + if (it->mode != FSTREE_MODE_HARD_LINK_RESOLVED) { + if (SZ_ADD_OV(fs->unique_inode_count, 1, &inum)) + goto fail_ov; + + if ((sizeof(size_t) > sizeof(sqfs_u32)) && + inum > 0x0FFFFFFFFUL) { + goto fail_ov; + } + + it->inode_num = (sqfs_u32)inum; + fs->unique_inode_count = inum; + } + } + + return 0; +fail_ov: + fputs("Too many inodes.\n", stderr); + return -1; +} + +static int resolve_hard_links_dfs(fstree_t *fs, tree_node_t *n) +{ + tree_node_t *it; + + if (n->mode == FSTREE_MODE_HARD_LINK) { + if (fstree_resolve_hard_link(fs, n)) + goto fail_link; + + assert(n->mode == FSTREE_MODE_HARD_LINK_RESOLVED); + it = n->data.target_node; + + if (S_ISDIR(it->mode) && it->data.dir.visited) + goto fail_link_loop; + } else if (S_ISDIR(n->mode)) { + n->data.dir.visited = true; + + for (it = n->data.dir.children; it != NULL; it = it->next) { + if (resolve_hard_links_dfs(fs, it)) + return -1; + } + + n->data.dir.visited = false; + } + + return 0; +fail_link: { + char *path = fstree_get_path(n); + fprintf(stderr, "Resolving hard link '%s' -> '%s': %s\n", + path == NULL ? n->name : path, n->data.target, + strerror(errno)); + free(path); +} + return -1; +fail_link_loop: { + char *npath = fstree_get_path(n); + char *tpath = fstree_get_path(it); + fprintf(stderr, "Hard link loop detected in '%s' -> '%s'\n", + npath == NULL ? n->name : npath, + tpath == NULL ? it->name : tpath); + free(npath); + free(tpath); +} + return -1; +} + +static file_info_t *file_list_dfs(tree_node_t *n) +{ + if (S_ISREG(n->mode)) { + n->data.file.next = NULL; + return &n->data.file; + } + + if (S_ISDIR(n->mode)) { + file_info_t *list = NULL, *last = NULL; + + for (n = n->data.dir.children; n != NULL; n = n->next) { + if (list == NULL) { + list = file_list_dfs(n); + if (list == NULL) + continue; + last = list; + } else { + last->next = file_list_dfs(n); + } + + while (last->next != NULL) + last = last->next; + } + + return list; + } + + return NULL; +} + +static void map_inodes_dfs(fstree_t *fs, tree_node_t *n) +{ + if (n->mode == FSTREE_MODE_HARD_LINK_RESOLVED) + return; + + fs->inodes[n->inode_num - 1] = n; + + if (S_ISDIR(n->mode)) { + for (n = n->data.dir.children; n != NULL; n = n->next) + map_inodes_dfs(fs, n); + } +} + +static void reorder_hard_links(fstree_t *fs) +{ + size_t i, j, tgt_idx; + tree_node_t *it, *tgt; + + for (i = 0; i < fs->unique_inode_count; ++i) { + if (!S_ISDIR(fs->inodes[i]->mode)) + continue; + + it = fs->inodes[i]->data.dir.children; + + for (; it != NULL; it = it->next) { + if (it->mode != FSTREE_MODE_HARD_LINK_RESOLVED) + continue; + + tgt = it->data.target_node; + tgt_idx = tgt->inode_num - 1; + + if (tgt_idx <= i) + continue; + + /* TODO ? */ + assert(!S_ISDIR(tgt->mode)); + + for (j = tgt_idx; j > i; --j) { + fs->inodes[j] = fs->inodes[j - 1]; + fs->inodes[j]->inode_num += 1; + } + + fs->inodes[i] = tgt; + + /* XXX: the possible overflow is checked for + during allocation */ + tgt->inode_num = (sqfs_u32)(i + 1); + ++i; + } + } +} + +int fstree_post_process(fstree_t *fs) +{ + size_t inum; + + if (resolve_hard_links_dfs(fs, fs->root)) + return -1; + + fs->unique_inode_count = 0; + + if (alloc_inode_num_dfs(fs, fs->root)) + return -1; + + if (SZ_ADD_OV(fs->unique_inode_count, 1, &inum)) + goto fail_root_ov; + + if ((sizeof(size_t) > sizeof(sqfs_u32)) && inum > 0x0FFFFFFFFUL) + goto fail_root_ov; + + fs->root->inode_num = (sqfs_u32)inum; + fs->unique_inode_count = inum; + + fs->inodes = calloc(sizeof(fs->inodes[0]), fs->unique_inode_count); + if (fs->inodes == NULL) { + perror("Allocating inode list"); + return -1; + } + + map_inodes_dfs(fs, fs->root); + reorder_hard_links(fs); + + fs->files = file_list_dfs(fs->root); + return 0; +fail_root_ov: + fputs("Too many inodes, cannot allocate number for root.\n", stderr); + return -1; +} diff --git a/lib/io/Makemodule.am b/lib/io/Makemodule.am index 918ce50..c331124 100644 --- a/lib/io/Makemodule.am +++ b/lib/io/Makemodule.am @@ -1,19 +1,16 @@ -libio_a_SOURCES = lib/io/internal.h -libio_a_SOURCES += include/io/istream.h lib/io/ostream.c lib/io/printf.c -libio_a_SOURCES += include/io/ostream.h lib/io/istream.c lib/io/get_line.c -libio_a_SOURCES += include/io/xfrm.h include/io/file.h include/io/std.h -libio_a_SOURCES += lib/io/xfrm/ostream.c lib/io/xfrm/istream.c +libio_a_SOURCES = include/io/istream.h include/io/ostream.h include/io/xfrm.h \ + include/io/file.h include/io/std.h \ + lib/io/src/internal.h lib/io/src/ostream.c lib/io/src/printf.c \ + lib/io/src/istream.c lib/io/src/get_line.c lib/io/src/xfrm/ostream.c \ + lib/io/src/xfrm/istream.c libio_a_CFLAGS = $(AM_CFLAGS) $(ZLIB_CFLAGS) $(XZ_CFLAGS) libio_a_CFLAGS += $(ZSTD_CFLAGS) $(BZIP2_CFLAGS) -libio_a_CPPFLAGS = $(AM_CPPFLAGS) if WINDOWS -libio_a_SOURCES += lib/io/win32/ostream.c -libio_a_SOURCES += lib/io/win32/istream.c +libio_a_SOURCES += lib/io/src/win32/ostream.c lib/io/src/win32/istream.c libio_a_CFLAGS += -DWINVER=0x0600 -D_WIN32_WINNT=0x0600 else -libio_a_SOURCES += lib/io/unix/ostream.c -libio_a_SOURCES += lib/io/unix/istream.c +libio_a_SOURCES += lib/io/src/unix/ostream.c lib/io/src/unix/istream.c endif noinst_LIBRARIES += libio.a diff --git a/lib/io/get_line.c b/lib/io/get_line.c deleted file mode 100644 index f7e0b59..0000000 --- a/lib/io/get_line.c +++ /dev/null @@ -1,118 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * get_line.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "internal.h" - -static void ltrim(char *buffer) -{ - size_t i = 0; - - while (isspace(buffer[i])) - ++i; - - if (i > 0) - memmove(buffer, buffer + i, strlen(buffer + i) + 1); -} - -static void rtrim(char *buffer) -{ - size_t i = strlen(buffer); - - while (i > 0 && isspace(buffer[i - 1])) - --i; - - buffer[i] = '\0'; -} - -static size_t trim(char *buffer, int flags) -{ - if (flags & ISTREAM_LINE_LTRIM) - ltrim(buffer); - - if (flags & ISTREAM_LINE_RTRIM) - rtrim(buffer); - - return strlen(buffer); -} - -int istream_get_line(istream_t *strm, char **out, - size_t *line_num, int flags) -{ - char *line = NULL, *new; - size_t i, line_len = 0; - bool have_line = false; - - *out = NULL; - - for (;;) { - if (istream_precache(strm)) - return -1; - - if (strm->buffer_used == 0) { - if (line_len == 0) - goto out_eof; - - line_len = trim(line, flags); - - if (line_len == 0 && - (flags & ISTREAM_LINE_SKIP_EMPTY)) { - goto out_eof; - } - break; - } - - for (i = 0; i < strm->buffer_used; ++i) { - if (strm->buffer[i] == '\n') - break; - } - - if (i < strm->buffer_used) { - have_line = true; - strm->buffer_offset = i + 1; - - if (i > 0 && strm->buffer[i - 1] == '\r') - --i; - } else { - strm->buffer_offset = i; - } - - new = realloc(line, line_len + i + 1); - if (new == NULL) - goto fail_errno; - - line = new; - memcpy(line + line_len, strm->buffer, i); - line_len += i; - line[line_len] = '\0'; - - if (have_line) { - line_len = trim(line, flags); - - if (line_len == 0 && - (flags & ISTREAM_LINE_SKIP_EMPTY)) { - free(line); - line = NULL; - have_line = false; - *line_num += 1; - continue; - } - break; - } - } - - *out = line; - return 0; -fail_errno: - fprintf(stderr, "%s: " PRI_SZ ": %s.\n", strm->get_filename(strm), - *line_num, strerror(errno)); - free(line); - *out = NULL; - return -1; -out_eof: - free(line); - *out = NULL; - return 1; -} diff --git a/lib/io/internal.h b/lib/io/internal.h deleted file mode 100644 index 25a0196..0000000 --- a/lib/io/internal.h +++ /dev/null @@ -1,31 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * internal.h - * - * Copyright (C) 2019 David Oberhollenzer - */ -#ifndef INTERNAL_H -#define INTERNAL_H - -#include "config.h" -#include "compat.h" -#include "io/istream.h" -#include "io/ostream.h" -#include "io/file.h" -#include "io/xfrm.h" -#include "io/std.h" -#include "xfrm/compress.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define BUFSZ (262144) - -#endif /* INTERNAL_H */ diff --git a/lib/io/istream.c b/lib/io/istream.c deleted file mode 100644 index 64fa478..0000000 --- a/lib/io/istream.c +++ /dev/null @@ -1,86 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * istream.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "internal.h" - - -sqfs_s32 istream_read(istream_t *strm, void *data, size_t size) -{ - sqfs_s32 total = 0; - size_t diff; - - if (size > 0x7FFFFFFF) - size = 0x7FFFFFFF; - - while (size > 0) { - if (strm->buffer_offset >= strm->buffer_used) { - if (istream_precache(strm)) - return -1; - - if (strm->buffer_used == 0) - break; - } - - diff = strm->buffer_used - strm->buffer_offset; - if (diff > size) - diff = size; - - memcpy(data, strm->buffer + strm->buffer_offset, diff); - data = (char *)data + diff; - strm->buffer_offset += diff; - size -= diff; - total += diff; - } - - return total; -} - -int istream_precache(istream_t *strm) -{ - if (strm->buffer_offset >= strm->buffer_used) { - strm->buffer_offset = 0; - strm->buffer_used = 0; - } else if (strm->buffer_offset > 0) { - memmove(strm->buffer, - strm->buffer + strm->buffer_offset, - strm->buffer_used - strm->buffer_offset); - - strm->buffer_used -= strm->buffer_offset; - strm->buffer_offset = 0; - } - - if (strm->eof) - return 0; - - return strm->precache(strm); -} - -int istream_skip(istream_t *strm, sqfs_u64 size) -{ - size_t diff; - - while (size > 0) { - if (strm->buffer_offset >= strm->buffer_used) { - if (istream_precache(strm)) - return -1; - - if (strm->buffer_used == 0) { - fprintf(stderr, "%s: unexpected end-of-file\n", - strm->get_filename(strm)); - return -1; - } - } - - diff = strm->buffer_used - strm->buffer_offset; - if ((sqfs_u64)diff > size) - diff = size; - - strm->buffer_offset += diff; - size -= diff; - } - - return 0; -} diff --git a/lib/io/ostream.c b/lib/io/ostream.c deleted file mode 100644 index da0b7b3..0000000 --- a/lib/io/ostream.c +++ /dev/null @@ -1,68 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * ostream.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "internal.h" - - -static int append_sparse_fallback(ostream_t *strm, size_t size) -{ - char buffer[512]; - size_t diff; - - memset(buffer, 0, sizeof(buffer)); - - while (size > 0) { - diff = size < sizeof(buffer) ? size : sizeof(buffer); - - if (strm->append(strm, buffer, diff)) - return -1; - - size -= diff; - } - - return 0; -} - -int ostream_append_sparse(ostream_t *strm, size_t size) -{ - if (strm->append_sparse == NULL) - return append_sparse_fallback(strm, size); - - return strm->append_sparse(strm, size); -} - -sqfs_s32 ostream_append_from_istream(ostream_t *out, istream_t *in, - sqfs_u32 size) -{ - sqfs_s32 total = 0; - size_t diff; - - if (size > 0x7FFFFFFF) - size = 0x7FFFFFFF; - - while (size > 0) { - if (in->buffer_offset >= in->buffer_used) { - if (istream_precache(in)) - return -1; - - if (in->buffer_used == 0) - break; - } - - diff = in->buffer_used - in->buffer_offset; - if (diff > size) - diff = size; - - if (out->append(out, in->buffer + in->buffer_offset, diff)) - return -1; - - in->buffer_offset += diff; - size -= diff; - total += diff; - } - - return total; -} diff --git a/lib/io/printf.c b/lib/io/printf.c deleted file mode 100644 index 3850487..0000000 --- a/lib/io/printf.c +++ /dev/null @@ -1,30 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * printf.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "internal.h" - -int ostream_printf(ostream_t *strm, const char *fmt, ...) -{ - char *temp = NULL; - va_list ap; - int ret; - - va_start(ap, fmt); - - ret = vasprintf(&temp, fmt, ap); - if (ret < 0) - perror(strm->get_filename(strm)); - va_end(ap); - - if (ret < 0) - return -1; - - if (strm->append(strm, temp, ret)) - ret = -1; - - free(temp); - return ret; -} diff --git a/lib/io/src/get_line.c b/lib/io/src/get_line.c new file mode 100644 index 0000000..f7e0b59 --- /dev/null +++ b/lib/io/src/get_line.c @@ -0,0 +1,118 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * get_line.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "internal.h" + +static void ltrim(char *buffer) +{ + size_t i = 0; + + while (isspace(buffer[i])) + ++i; + + if (i > 0) + memmove(buffer, buffer + i, strlen(buffer + i) + 1); +} + +static void rtrim(char *buffer) +{ + size_t i = strlen(buffer); + + while (i > 0 && isspace(buffer[i - 1])) + --i; + + buffer[i] = '\0'; +} + +static size_t trim(char *buffer, int flags) +{ + if (flags & ISTREAM_LINE_LTRIM) + ltrim(buffer); + + if (flags & ISTREAM_LINE_RTRIM) + rtrim(buffer); + + return strlen(buffer); +} + +int istream_get_line(istream_t *strm, char **out, + size_t *line_num, int flags) +{ + char *line = NULL, *new; + size_t i, line_len = 0; + bool have_line = false; + + *out = NULL; + + for (;;) { + if (istream_precache(strm)) + return -1; + + if (strm->buffer_used == 0) { + if (line_len == 0) + goto out_eof; + + line_len = trim(line, flags); + + if (line_len == 0 && + (flags & ISTREAM_LINE_SKIP_EMPTY)) { + goto out_eof; + } + break; + } + + for (i = 0; i < strm->buffer_used; ++i) { + if (strm->buffer[i] == '\n') + break; + } + + if (i < strm->buffer_used) { + have_line = true; + strm->buffer_offset = i + 1; + + if (i > 0 && strm->buffer[i - 1] == '\r') + --i; + } else { + strm->buffer_offset = i; + } + + new = realloc(line, line_len + i + 1); + if (new == NULL) + goto fail_errno; + + line = new; + memcpy(line + line_len, strm->buffer, i); + line_len += i; + line[line_len] = '\0'; + + if (have_line) { + line_len = trim(line, flags); + + if (line_len == 0 && + (flags & ISTREAM_LINE_SKIP_EMPTY)) { + free(line); + line = NULL; + have_line = false; + *line_num += 1; + continue; + } + break; + } + } + + *out = line; + return 0; +fail_errno: + fprintf(stderr, "%s: " PRI_SZ ": %s.\n", strm->get_filename(strm), + *line_num, strerror(errno)); + free(line); + *out = NULL; + return -1; +out_eof: + free(line); + *out = NULL; + return 1; +} diff --git a/lib/io/src/internal.h b/lib/io/src/internal.h new file mode 100644 index 0000000..25a0196 --- /dev/null +++ b/lib/io/src/internal.h @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * internal.h + * + * Copyright (C) 2019 David Oberhollenzer + */ +#ifndef INTERNAL_H +#define INTERNAL_H + +#include "config.h" +#include "compat.h" +#include "io/istream.h" +#include "io/ostream.h" +#include "io/file.h" +#include "io/xfrm.h" +#include "io/std.h" +#include "xfrm/compress.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define BUFSZ (262144) + +#endif /* INTERNAL_H */ diff --git a/lib/io/src/istream.c b/lib/io/src/istream.c new file mode 100644 index 0000000..64fa478 --- /dev/null +++ b/lib/io/src/istream.c @@ -0,0 +1,86 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * istream.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "internal.h" + + +sqfs_s32 istream_read(istream_t *strm, void *data, size_t size) +{ + sqfs_s32 total = 0; + size_t diff; + + if (size > 0x7FFFFFFF) + size = 0x7FFFFFFF; + + while (size > 0) { + if (strm->buffer_offset >= strm->buffer_used) { + if (istream_precache(strm)) + return -1; + + if (strm->buffer_used == 0) + break; + } + + diff = strm->buffer_used - strm->buffer_offset; + if (diff > size) + diff = size; + + memcpy(data, strm->buffer + strm->buffer_offset, diff); + data = (char *)data + diff; + strm->buffer_offset += diff; + size -= diff; + total += diff; + } + + return total; +} + +int istream_precache(istream_t *strm) +{ + if (strm->buffer_offset >= strm->buffer_used) { + strm->buffer_offset = 0; + strm->buffer_used = 0; + } else if (strm->buffer_offset > 0) { + memmove(strm->buffer, + strm->buffer + strm->buffer_offset, + strm->buffer_used - strm->buffer_offset); + + strm->buffer_used -= strm->buffer_offset; + strm->buffer_offset = 0; + } + + if (strm->eof) + return 0; + + return strm->precache(strm); +} + +int istream_skip(istream_t *strm, sqfs_u64 size) +{ + size_t diff; + + while (size > 0) { + if (strm->buffer_offset >= strm->buffer_used) { + if (istream_precache(strm)) + return -1; + + if (strm->buffer_used == 0) { + fprintf(stderr, "%s: unexpected end-of-file\n", + strm->get_filename(strm)); + return -1; + } + } + + diff = strm->buffer_used - strm->buffer_offset; + if ((sqfs_u64)diff > size) + diff = size; + + strm->buffer_offset += diff; + size -= diff; + } + + return 0; +} diff --git a/lib/io/src/ostream.c b/lib/io/src/ostream.c new file mode 100644 index 0000000..da0b7b3 --- /dev/null +++ b/lib/io/src/ostream.c @@ -0,0 +1,68 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * ostream.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "internal.h" + + +static int append_sparse_fallback(ostream_t *strm, size_t size) +{ + char buffer[512]; + size_t diff; + + memset(buffer, 0, sizeof(buffer)); + + while (size > 0) { + diff = size < sizeof(buffer) ? size : sizeof(buffer); + + if (strm->append(strm, buffer, diff)) + return -1; + + size -= diff; + } + + return 0; +} + +int ostream_append_sparse(ostream_t *strm, size_t size) +{ + if (strm->append_sparse == NULL) + return append_sparse_fallback(strm, size); + + return strm->append_sparse(strm, size); +} + +sqfs_s32 ostream_append_from_istream(ostream_t *out, istream_t *in, + sqfs_u32 size) +{ + sqfs_s32 total = 0; + size_t diff; + + if (size > 0x7FFFFFFF) + size = 0x7FFFFFFF; + + while (size > 0) { + if (in->buffer_offset >= in->buffer_used) { + if (istream_precache(in)) + return -1; + + if (in->buffer_used == 0) + break; + } + + diff = in->buffer_used - in->buffer_offset; + if (diff > size) + diff = size; + + if (out->append(out, in->buffer + in->buffer_offset, diff)) + return -1; + + in->buffer_offset += diff; + size -= diff; + total += diff; + } + + return total; +} diff --git a/lib/io/src/printf.c b/lib/io/src/printf.c new file mode 100644 index 0000000..3850487 --- /dev/null +++ b/lib/io/src/printf.c @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * printf.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "internal.h" + +int ostream_printf(ostream_t *strm, const char *fmt, ...) +{ + char *temp = NULL; + va_list ap; + int ret; + + va_start(ap, fmt); + + ret = vasprintf(&temp, fmt, ap); + if (ret < 0) + perror(strm->get_filename(strm)); + va_end(ap); + + if (ret < 0) + return -1; + + if (strm->append(strm, temp, ret)) + ret = -1; + + free(temp); + return ret; +} diff --git a/lib/io/src/unix/istream.c b/lib/io/src/unix/istream.c new file mode 100644 index 0000000..f8cffad --- /dev/null +++ b/lib/io/src/unix/istream.c @@ -0,0 +1,123 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * istream.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "../internal.h" + +typedef struct { + istream_t base; + char *path; + int fd; + + sqfs_u8 buffer[BUFSZ]; +} file_istream_t; + +static int file_precache(istream_t *strm) +{ + file_istream_t *file = (file_istream_t *)strm; + ssize_t ret; + size_t diff; + + while (strm->buffer_used < sizeof(file->buffer)) { + diff = sizeof(file->buffer) - strm->buffer_used; + + ret = read(file->fd, strm->buffer + strm->buffer_used, diff); + + if (ret == 0) { + strm->eof = true; + break; + } + + if (ret < 0) { + if (errno == EINTR) + continue; + + perror(file->path); + return -1; + } + + strm->buffer_used += ret; + } + + return 0; +} + +static const char *file_get_filename(istream_t *strm) +{ + file_istream_t *file = (file_istream_t *)strm; + + return file->path; +} + +static void file_destroy(sqfs_object_t *obj) +{ + file_istream_t *file = (file_istream_t *)obj; + + if (file->fd != STDIN_FILENO) + close(file->fd); + + free(file->path); + free(file); +} + +istream_t *istream_open_file(const char *path) +{ + file_istream_t *file = calloc(1, sizeof(*file)); + istream_t *strm = (istream_t *)file; + + if (file == NULL) { + perror(path); + return NULL; + } + + sqfs_object_init(file, file_destroy, NULL); + + file->path = strdup(path); + if (file->path == NULL) { + perror(path); + goto fail_free; + } + + file->fd = open(path, O_RDONLY); + if (file->fd < 0) { + perror(path); + goto fail_path; + } + + strm->buffer = file->buffer; + strm->precache = file_precache; + strm->get_filename = file_get_filename; + return strm; +fail_path: + free(file->path); +fail_free: + free(file); + return NULL; +} + +istream_t *istream_open_stdin(void) +{ + file_istream_t *file = calloc(1, sizeof(*file)); + istream_t *strm = (istream_t *)file; + + if (file == NULL) + goto fail; + + sqfs_object_init(file, file_destroy, NULL); + + file->path = strdup("stdin"); + if (file->path == NULL) + goto fail; + + file->fd = STDIN_FILENO; + strm->buffer = file->buffer; + strm->precache = file_precache; + strm->get_filename = file_get_filename; + return strm; +fail: + perror("creating file wrapper for stdin"); + free(file); + return NULL; +} diff --git a/lib/io/src/unix/ostream.c b/lib/io/src/unix/ostream.c new file mode 100644 index 0000000..5ef2af2 --- /dev/null +++ b/lib/io/src/unix/ostream.c @@ -0,0 +1,173 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * ostream.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "../internal.h" + +typedef struct { + ostream_t base; + char *path; + int fd; + + off_t sparse_count; + off_t size; +} file_ostream_t; + +static int file_append(ostream_t *strm, const void *data, size_t size) +{ + file_ostream_t *file = (file_ostream_t *)strm; + ssize_t ret; + + if (size == 0) + return 0; + + if (file->sparse_count > 0) { + if (lseek(file->fd, file->sparse_count, SEEK_CUR) == (off_t)-1) + goto fail_errno; + + file->sparse_count = 0; + } + + while (size > 0) { + ret = write(file->fd, data, size); + + if (ret == 0) { + fprintf(stderr, "%s: truncated data write.\n", + file->path); + return -1; + } + + if (ret < 0) { + if (errno == EINTR) + continue; + goto fail_errno; + } + + file->size += ret; + size -= ret; + data = (const char *)data + ret; + } + + return 0; +fail_errno: + perror(file->path); + return -1; +} + +static int file_append_sparse(ostream_t *strm, size_t size) +{ + file_ostream_t *file = (file_ostream_t *)strm; + + file->sparse_count += size; + file->size += size; + return 0; +} + +static int file_flush(ostream_t *strm) +{ + file_ostream_t *file = (file_ostream_t *)strm; + + if (file->sparse_count > 0) { + if (ftruncate(file->fd, file->size) != 0) + goto fail; + } + + if (fsync(file->fd) != 0) { + if (errno == EINVAL) + return 0; + goto fail; + } + + return 0; +fail: + perror(file->path); + return -1; +} + +static void file_destroy(sqfs_object_t *obj) +{ + file_ostream_t *file = (file_ostream_t *)obj; + + if (file->fd != STDOUT_FILENO) + close(file->fd); + + free(file->path); + free(file); +} + +static const char *file_get_filename(ostream_t *strm) +{ + file_ostream_t *file = (file_ostream_t *)strm; + + return file->path; +} + +ostream_t *ostream_open_file(const char *path, int flags) +{ + file_ostream_t *file = calloc(1, sizeof(*file)); + ostream_t *strm = (ostream_t *)file; + + if (file == NULL) { + perror(path); + return NULL; + } + + sqfs_object_init(file, file_destroy, NULL); + + file->path = strdup(path); + if (file->path == NULL) { + perror(path); + goto fail_free; + } + + if (flags & OSTREAM_OPEN_OVERWRITE) { + file->fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644); + } else { + file->fd = open(path, O_WRONLY | O_CREAT | O_EXCL, 0644); + } + + if (file->fd < 0) { + perror(path); + goto fail_path; + } + + if (flags & OSTREAM_OPEN_SPARSE) + strm->append_sparse = file_append_sparse; + + strm->append = file_append; + strm->flush = file_flush; + strm->get_filename = file_get_filename; + return strm; +fail_path: + free(file->path); +fail_free: + free(file); + return NULL; +} + +ostream_t *ostream_open_stdout(void) +{ + file_ostream_t *file = calloc(1, sizeof(*file)); + ostream_t *strm = (ostream_t *)file; + + if (file == NULL) + goto fail; + + file->path = strdup("stdout"); + if (file->path == NULL) + goto fail; + + file->fd = STDOUT_FILENO; + strm->append = file_append; + strm->flush = file_flush; + strm->get_filename = file_get_filename; + + sqfs_object_init(file, file_destroy, NULL); + return strm; +fail: + perror("creating file wrapper for stdout"); + free(file); + return NULL; +} diff --git a/lib/io/src/win32/istream.c b/lib/io/src/win32/istream.c new file mode 100644 index 0000000..be3d829 --- /dev/null +++ b/lib/io/src/win32/istream.c @@ -0,0 +1,138 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * istream.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "../internal.h" + +#define WIN32_LEAN_AND_MEAN +#include + +typedef struct { + istream_t base; + char *path; + HANDLE hnd; + + sqfs_u8 buffer[BUFSZ]; +} file_istream_t; + +static int file_precache(istream_t *strm) +{ + file_istream_t *file = (file_istream_t *)strm; + DWORD diff, actual; + HANDLE hnd; + + hnd = file->path == NULL ? GetStdHandle(STD_INPUT_HANDLE) : file->hnd; + + while (strm->buffer_used < sizeof(file->buffer)) { + diff = sizeof(file->buffer) - strm->buffer_used; + + if (!ReadFile(hnd, strm->buffer + strm->buffer_used, + diff, &actual, NULL)) { + DWORD error = GetLastError(); + + if (error == ERROR_HANDLE_EOF || + error == ERROR_BROKEN_PIPE) { + strm->eof = true; + break; + } + + SetLastError(error); + + w32_perror(file->path == NULL ? "stdin" : file->path); + return -1; + } + + if (actual == 0) { + strm->eof = true; + break; + } + + strm->buffer_used += actual; + } + + return 0; +} + +static const char *file_get_filename(istream_t *strm) +{ + file_istream_t *file = (file_istream_t *)strm; + + return file->path == NULL ? "stdin" : file->path; +} + +static void file_destroy(sqfs_object_t *obj) +{ + file_istream_t *file = (file_istream_t *)obj; + + if (file->path != NULL) { + CloseHandle(file->hnd); + free(file->path); + } + + free(file); +} + +istream_t *istream_open_file(const char *path) +{ + file_istream_t *file = calloc(1, sizeof(*file)); + istream_t *strm = (istream_t *)file; + WCHAR *wpath = NULL; + + if (file == NULL) { + perror(path); + return NULL; + } + + sqfs_object_init(file, file_destroy, NULL); + + wpath = path_to_windows(path); + if (wpath == NULL) + goto fail_free; + + file->path = strdup(path); + if (file->path == NULL) { + perror(path); + goto fail_free; + } + + file->hnd = CreateFileW(wpath, GENERIC_READ, FILE_SHARE_READ, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + + if (file->hnd == INVALID_HANDLE_VALUE) { + perror(path); + goto fail_path; + } + + free(wpath); + + strm->buffer = file->buffer; + strm->precache = file_precache; + strm->get_filename = file_get_filename; + return strm; +fail_path: + free(file->path); +fail_free: + free(wpath); + free(file); + return NULL; +} + +istream_t *istream_open_stdin(void) +{ + file_istream_t *file = calloc(1, sizeof(*file)); + istream_t *strm = (istream_t *)file; + + if (file == NULL) { + perror("stdin"); + return NULL; + } + + sqfs_object_init(file, file_destroy, NULL); + + strm->buffer = file->buffer; + strm->precache = file_precache; + strm->get_filename = file_get_filename; + return strm; +} diff --git a/lib/io/src/win32/ostream.c b/lib/io/src/win32/ostream.c new file mode 100644 index 0000000..0fe04f3 --- /dev/null +++ b/lib/io/src/win32/ostream.c @@ -0,0 +1,199 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * ostream.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "../internal.h" + +#define WIN32_LEAN_AND_MEAN +#include + +typedef struct { + ostream_t base; + char *path; + HANDLE hnd; +} file_ostream_t; + +static int w32_append(HANDLE hnd, const char *filename, + const void *data, size_t size) +{ + DWORD diff; + + while (size > 0) { + if (!WriteFile(hnd, data, size, &diff, NULL)) { + w32_perror(filename); + return -1; + } + + size -= diff; + data = (const char *)data + diff; + } + + return 0; +} + +static int w32_flush(HANDLE hnd, const char *filename) +{ + if (!FlushFileBuffers(hnd)) { + w32_perror(filename); + return -1; + } + + return 0; +} + +/*****************************************************************************/ + +static int file_append(ostream_t *strm, const void *data, size_t size) +{ + file_ostream_t *file = (file_ostream_t *)strm; + + return w32_append(file->hnd, file->path, data, size); +} + +static int file_append_sparse(ostream_t *strm, size_t size) +{ + file_ostream_t *file = (file_ostream_t *)strm; + LARGE_INTEGER pos; + + pos.QuadPart = size; + + if (!SetFilePointerEx(file->hnd, pos, NULL, FILE_CURRENT)) + goto fail; + + if (!SetEndOfFile(file->hnd)) + goto fail; + + return 0; +fail: + w32_perror(file->path); + return -1; +} + +static int file_flush(ostream_t *strm) +{ + file_ostream_t *file = (file_ostream_t *)strm; + + return w32_flush(file->hnd, file->path); +} + +static void file_destroy(sqfs_object_t *obj) +{ + file_ostream_t *file = (file_ostream_t *)obj; + + CloseHandle(file->hnd); + free(file->path); + free(file); +} + +static const char *file_get_filename(ostream_t *strm) +{ + file_ostream_t *file = (file_ostream_t *)strm; + + return file->path; +} + +/*****************************************************************************/ + +static int stdout_append(ostream_t *strm, const void *data, size_t size) +{ + (void)strm; + return w32_append(GetStdHandle(STD_OUTPUT_HANDLE), "stdout", + data, size); +} + +static int stdout_flush(ostream_t *strm) +{ + (void)strm; + return w32_flush(GetStdHandle(STD_OUTPUT_HANDLE), "stdout"); +} + +static void stdout_destroy(sqfs_object_t *obj) +{ + free(obj); +} + +static const char *stdout_get_filename(ostream_t *strm) +{ + (void)strm; + return "stdout"; +} + +/*****************************************************************************/ + +ostream_t *ostream_open_file(const char *path, int flags) +{ + file_ostream_t *file = calloc(1, sizeof(*file)); + sqfs_object_t *obj = (sqfs_object_t *)file; + ostream_t *strm = (ostream_t *)file; + int access_flags, creation_mode; + WCHAR *wpath = NULL; + + if (file == NULL) { + perror(path); + return NULL; + } + + sqfs_object_init(file, file_destroy, NULL); + + wpath = path_to_windows(path); + if (wpath == NULL) + goto fail_free; + + file->path = strdup(path); + if (file->path == NULL) { + perror(path); + goto fail_free; + } + + access_flags = GENERIC_WRITE; + + if (flags & OSTREAM_OPEN_OVERWRITE) { + creation_mode = CREATE_ALWAYS; + } else { + creation_mode = CREATE_NEW; + } + + file->hnd = CreateFileW(wpath, access_flags, 0, NULL, creation_mode, + FILE_ATTRIBUTE_NORMAL, NULL); + + if (file->hnd == INVALID_HANDLE_VALUE) { + w32_perror(path); + goto fail_path; + } + + free(wpath); + + if (flags & OSTREAM_OPEN_SPARSE) + strm->append_sparse = file_append_sparse; + + strm->append = file_append; + strm->flush = file_flush; + strm->get_filename = file_get_filename; + return strm; +fail_path: + free(file->path); +fail_free: + free(file); + free(wpath); + return NULL; +} + +ostream_t *ostream_open_stdout(void) +{ + ostream_t *strm = calloc(1, sizeof(*strm)); + sqfs_object_t *obj = (sqfs_object_t *)strm; + + if (strm == NULL) { + perror("creating stdout file wrapper"); + return NULL; + } + + sqfs_object_init(strm, stdout_destroy, NULL); + + strm->append = stdout_append; + strm->flush = stdout_flush; + strm->get_filename = stdout_get_filename; + return strm; +} diff --git a/lib/io/src/xfrm/istream.c b/lib/io/src/xfrm/istream.c new file mode 100644 index 0000000..4a1ad77 --- /dev/null +++ b/lib/io/src/xfrm/istream.c @@ -0,0 +1,106 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * istream.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "../internal.h" + +typedef struct istream_xfrm_t { + istream_t base; + + istream_t *wrapped; + xfrm_stream_t *xfrm; + + sqfs_u8 uncompressed[BUFSZ]; +} istream_xfrm_t; + +static int xfrm_precache(istream_t *base) +{ + istream_xfrm_t *xfrm = (istream_xfrm_t *)base; + int ret; + + ret = istream_precache(xfrm->wrapped); + if (ret != 0) + return ret; + + for (;;) { + const sqfs_u32 in_sz = xfrm->wrapped->buffer_used; + const sqfs_u32 out_sz = sizeof(xfrm->uncompressed); + sqfs_u32 in_off = 0, out_off = base->buffer_used; + int mode = XFRM_STREAM_FLUSH_NONE; + + if (xfrm->wrapped->eof) + mode = XFRM_STREAM_FLUSH_FULL; + + ret = xfrm->xfrm->process_data(xfrm->xfrm, + xfrm->wrapped->buffer, in_sz, + base->buffer + out_off, + out_sz - out_off, + &in_off, &out_off, mode); + + if (ret == XFRM_STREAM_ERROR) { + fprintf(stderr, "%s: internal error in decompressor.\n", + base->get_filename(base)); + return -1; + } + + base->buffer_used = out_off; + xfrm->wrapped->buffer_offset = in_off; + + if (ret == XFRM_STREAM_BUFFER_FULL || out_off >= out_sz) + break; + + ret = istream_precache(xfrm->wrapped); + if (ret != 0) + return ret; + + if (xfrm->wrapped->eof && xfrm->wrapped->buffer_used == 0) { + if (base->buffer_used == 0) + base->eof = true; + break; + } + } + + return 0; +} + +static const char *xfrm_get_filename(istream_t *strm) +{ + istream_xfrm_t *xfrm = (istream_xfrm_t *)strm; + + return xfrm->wrapped->get_filename(xfrm->wrapped); +} + +static void xfrm_destroy(sqfs_object_t *obj) +{ + istream_xfrm_t *xfrm = (istream_xfrm_t *)obj; + + sqfs_drop(xfrm->xfrm); + sqfs_drop(xfrm->wrapped); + free(xfrm); +} + +istream_t *istream_xfrm_create(istream_t *strm, xfrm_stream_t *xfrm) +{ + istream_xfrm_t *stream = calloc(1, sizeof(*stream)); + istream_t *base = (istream_t *)stream; + + if (stream == NULL) + goto fail; + + sqfs_object_init(stream, xfrm_destroy, NULL); + + stream->wrapped = sqfs_grab(strm); + stream->xfrm = sqfs_grab(xfrm); + + base->precache = xfrm_precache; + base->get_filename = xfrm_get_filename; + base->buffer = stream->uncompressed; + base->eof = false; + return base; +fail: + fprintf(stderr, "%s: error initializing decompressor stream.\n", + strm->get_filename(strm)); + return NULL; +} diff --git a/lib/io/src/xfrm/ostream.c b/lib/io/src/xfrm/ostream.c new file mode 100644 index 0000000..bd94515 --- /dev/null +++ b/lib/io/src/xfrm/ostream.c @@ -0,0 +1,144 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * ostream.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "../internal.h" + +typedef struct ostream_xfrm_t { + ostream_t base; + + ostream_t *wrapped; + xfrm_stream_t *xfrm; + + size_t inbuf_used; + + sqfs_u8 inbuf[BUFSZ]; + sqfs_u8 outbuf[BUFSZ]; +} ostream_xfrm_t; + +static int flush_inbuf(ostream_xfrm_t *xfrm, bool finish) +{ + const sqfs_u32 avail_out = sizeof(xfrm->outbuf); + const sqfs_u32 avail_in = xfrm->inbuf_used; + const int mode = finish ? XFRM_STREAM_FLUSH_FULL : + XFRM_STREAM_FLUSH_NONE; + sqfs_u32 off_in = 0, off_out = 0; + int ret; + + while (finish || off_in < avail_in) { + ret = xfrm->xfrm->process_data(xfrm->xfrm, + xfrm->inbuf + off_in, + avail_in - off_in, + xfrm->outbuf + off_out, + avail_out - off_out, + &off_in, &off_out, mode); + + if (ret == XFRM_STREAM_ERROR) { + fprintf(stderr, + "%s: internal error in compressor.\n", + xfrm->wrapped->get_filename(xfrm->wrapped)); + return -1; + } + + if (ostream_append(xfrm->wrapped, xfrm->outbuf, off_out)) + return -1; + + off_out = 0; + + if (ret == XFRM_STREAM_END) + break; + } + + if (off_out > 0) { + if (ostream_append(xfrm->wrapped, xfrm->outbuf, off_out)) + return -1; + } + + if (off_in < avail_in) { + memmove(xfrm->inbuf, xfrm->inbuf + off_in, avail_in - off_in); + xfrm->inbuf_used -= off_in; + } else { + xfrm->inbuf_used = 0; + } + + return 0; +} + +static int xfrm_append(ostream_t *strm, const void *data, size_t size) +{ + ostream_xfrm_t *xfrm = (ostream_xfrm_t *)strm; + size_t diff; + + while (size > 0) { + if (xfrm->inbuf_used >= BUFSZ) { + if (flush_inbuf(xfrm, false)) + return -1; + } + + diff = BUFSZ - xfrm->inbuf_used; + + if (diff > size) + diff = size; + + memcpy(xfrm->inbuf + xfrm->inbuf_used, data, diff); + + xfrm->inbuf_used += diff; + data = (const char *)data + diff; + size -= diff; + } + + return 0; +} + +static int xfrm_flush(ostream_t *strm) +{ + ostream_xfrm_t *xfrm = (ostream_xfrm_t *)strm; + + if (xfrm->inbuf_used > 0) { + if (flush_inbuf(xfrm, true)) + return -1; + } + + return xfrm->wrapped->flush(xfrm->wrapped); +} + +static const char *xfrm_get_filename(ostream_t *strm) +{ + ostream_xfrm_t *xfrm = (ostream_xfrm_t *)strm; + + return xfrm->wrapped->get_filename(xfrm->wrapped); +} + +static void xfrm_destroy(sqfs_object_t *obj) +{ + ostream_xfrm_t *xfrm = (ostream_xfrm_t *)obj; + + sqfs_drop(xfrm->wrapped); + sqfs_drop(xfrm->xfrm); + free(xfrm); +} + +ostream_t *ostream_xfrm_create(ostream_t *strm, xfrm_stream_t *xfrm) +{ + ostream_xfrm_t *stream = calloc(1, sizeof(*stream)); + ostream_t *base = (ostream_t *)stream; + + if (stream == NULL) + goto fail; + + sqfs_object_init(stream, xfrm_destroy, NULL); + + stream->wrapped = sqfs_grab(strm); + stream->xfrm = sqfs_grab(xfrm); + stream->inbuf_used = 0; + base->append = xfrm_append; + base->flush = xfrm_flush; + base->get_filename = xfrm_get_filename; + return base; +fail: + fprintf(stderr, "%s: error initializing compressor.\n", + strm->get_filename(strm)); + return NULL; +} diff --git a/lib/io/unix/istream.c b/lib/io/unix/istream.c deleted file mode 100644 index f8cffad..0000000 --- a/lib/io/unix/istream.c +++ /dev/null @@ -1,123 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * istream.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "../internal.h" - -typedef struct { - istream_t base; - char *path; - int fd; - - sqfs_u8 buffer[BUFSZ]; -} file_istream_t; - -static int file_precache(istream_t *strm) -{ - file_istream_t *file = (file_istream_t *)strm; - ssize_t ret; - size_t diff; - - while (strm->buffer_used < sizeof(file->buffer)) { - diff = sizeof(file->buffer) - strm->buffer_used; - - ret = read(file->fd, strm->buffer + strm->buffer_used, diff); - - if (ret == 0) { - strm->eof = true; - break; - } - - if (ret < 0) { - if (errno == EINTR) - continue; - - perror(file->path); - return -1; - } - - strm->buffer_used += ret; - } - - return 0; -} - -static const char *file_get_filename(istream_t *strm) -{ - file_istream_t *file = (file_istream_t *)strm; - - return file->path; -} - -static void file_destroy(sqfs_object_t *obj) -{ - file_istream_t *file = (file_istream_t *)obj; - - if (file->fd != STDIN_FILENO) - close(file->fd); - - free(file->path); - free(file); -} - -istream_t *istream_open_file(const char *path) -{ - file_istream_t *file = calloc(1, sizeof(*file)); - istream_t *strm = (istream_t *)file; - - if (file == NULL) { - perror(path); - return NULL; - } - - sqfs_object_init(file, file_destroy, NULL); - - file->path = strdup(path); - if (file->path == NULL) { - perror(path); - goto fail_free; - } - - file->fd = open(path, O_RDONLY); - if (file->fd < 0) { - perror(path); - goto fail_path; - } - - strm->buffer = file->buffer; - strm->precache = file_precache; - strm->get_filename = file_get_filename; - return strm; -fail_path: - free(file->path); -fail_free: - free(file); - return NULL; -} - -istream_t *istream_open_stdin(void) -{ - file_istream_t *file = calloc(1, sizeof(*file)); - istream_t *strm = (istream_t *)file; - - if (file == NULL) - goto fail; - - sqfs_object_init(file, file_destroy, NULL); - - file->path = strdup("stdin"); - if (file->path == NULL) - goto fail; - - file->fd = STDIN_FILENO; - strm->buffer = file->buffer; - strm->precache = file_precache; - strm->get_filename = file_get_filename; - return strm; -fail: - perror("creating file wrapper for stdin"); - free(file); - return NULL; -} diff --git a/lib/io/unix/ostream.c b/lib/io/unix/ostream.c deleted file mode 100644 index 5ef2af2..0000000 --- a/lib/io/unix/ostream.c +++ /dev/null @@ -1,173 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * ostream.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "../internal.h" - -typedef struct { - ostream_t base; - char *path; - int fd; - - off_t sparse_count; - off_t size; -} file_ostream_t; - -static int file_append(ostream_t *strm, const void *data, size_t size) -{ - file_ostream_t *file = (file_ostream_t *)strm; - ssize_t ret; - - if (size == 0) - return 0; - - if (file->sparse_count > 0) { - if (lseek(file->fd, file->sparse_count, SEEK_CUR) == (off_t)-1) - goto fail_errno; - - file->sparse_count = 0; - } - - while (size > 0) { - ret = write(file->fd, data, size); - - if (ret == 0) { - fprintf(stderr, "%s: truncated data write.\n", - file->path); - return -1; - } - - if (ret < 0) { - if (errno == EINTR) - continue; - goto fail_errno; - } - - file->size += ret; - size -= ret; - data = (const char *)data + ret; - } - - return 0; -fail_errno: - perror(file->path); - return -1; -} - -static int file_append_sparse(ostream_t *strm, size_t size) -{ - file_ostream_t *file = (file_ostream_t *)strm; - - file->sparse_count += size; - file->size += size; - return 0; -} - -static int file_flush(ostream_t *strm) -{ - file_ostream_t *file = (file_ostream_t *)strm; - - if (file->sparse_count > 0) { - if (ftruncate(file->fd, file->size) != 0) - goto fail; - } - - if (fsync(file->fd) != 0) { - if (errno == EINVAL) - return 0; - goto fail; - } - - return 0; -fail: - perror(file->path); - return -1; -} - -static void file_destroy(sqfs_object_t *obj) -{ - file_ostream_t *file = (file_ostream_t *)obj; - - if (file->fd != STDOUT_FILENO) - close(file->fd); - - free(file->path); - free(file); -} - -static const char *file_get_filename(ostream_t *strm) -{ - file_ostream_t *file = (file_ostream_t *)strm; - - return file->path; -} - -ostream_t *ostream_open_file(const char *path, int flags) -{ - file_ostream_t *file = calloc(1, sizeof(*file)); - ostream_t *strm = (ostream_t *)file; - - if (file == NULL) { - perror(path); - return NULL; - } - - sqfs_object_init(file, file_destroy, NULL); - - file->path = strdup(path); - if (file->path == NULL) { - perror(path); - goto fail_free; - } - - if (flags & OSTREAM_OPEN_OVERWRITE) { - file->fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644); - } else { - file->fd = open(path, O_WRONLY | O_CREAT | O_EXCL, 0644); - } - - if (file->fd < 0) { - perror(path); - goto fail_path; - } - - if (flags & OSTREAM_OPEN_SPARSE) - strm->append_sparse = file_append_sparse; - - strm->append = file_append; - strm->flush = file_flush; - strm->get_filename = file_get_filename; - return strm; -fail_path: - free(file->path); -fail_free: - free(file); - return NULL; -} - -ostream_t *ostream_open_stdout(void) -{ - file_ostream_t *file = calloc(1, sizeof(*file)); - ostream_t *strm = (ostream_t *)file; - - if (file == NULL) - goto fail; - - file->path = strdup("stdout"); - if (file->path == NULL) - goto fail; - - file->fd = STDOUT_FILENO; - strm->append = file_append; - strm->flush = file_flush; - strm->get_filename = file_get_filename; - - sqfs_object_init(file, file_destroy, NULL); - return strm; -fail: - perror("creating file wrapper for stdout"); - free(file); - return NULL; -} diff --git a/lib/io/win32/istream.c b/lib/io/win32/istream.c deleted file mode 100644 index be3d829..0000000 --- a/lib/io/win32/istream.c +++ /dev/null @@ -1,138 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * istream.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "../internal.h" - -#define WIN32_LEAN_AND_MEAN -#include - -typedef struct { - istream_t base; - char *path; - HANDLE hnd; - - sqfs_u8 buffer[BUFSZ]; -} file_istream_t; - -static int file_precache(istream_t *strm) -{ - file_istream_t *file = (file_istream_t *)strm; - DWORD diff, actual; - HANDLE hnd; - - hnd = file->path == NULL ? GetStdHandle(STD_INPUT_HANDLE) : file->hnd; - - while (strm->buffer_used < sizeof(file->buffer)) { - diff = sizeof(file->buffer) - strm->buffer_used; - - if (!ReadFile(hnd, strm->buffer + strm->buffer_used, - diff, &actual, NULL)) { - DWORD error = GetLastError(); - - if (error == ERROR_HANDLE_EOF || - error == ERROR_BROKEN_PIPE) { - strm->eof = true; - break; - } - - SetLastError(error); - - w32_perror(file->path == NULL ? "stdin" : file->path); - return -1; - } - - if (actual == 0) { - strm->eof = true; - break; - } - - strm->buffer_used += actual; - } - - return 0; -} - -static const char *file_get_filename(istream_t *strm) -{ - file_istream_t *file = (file_istream_t *)strm; - - return file->path == NULL ? "stdin" : file->path; -} - -static void file_destroy(sqfs_object_t *obj) -{ - file_istream_t *file = (file_istream_t *)obj; - - if (file->path != NULL) { - CloseHandle(file->hnd); - free(file->path); - } - - free(file); -} - -istream_t *istream_open_file(const char *path) -{ - file_istream_t *file = calloc(1, sizeof(*file)); - istream_t *strm = (istream_t *)file; - WCHAR *wpath = NULL; - - if (file == NULL) { - perror(path); - return NULL; - } - - sqfs_object_init(file, file_destroy, NULL); - - wpath = path_to_windows(path); - if (wpath == NULL) - goto fail_free; - - file->path = strdup(path); - if (file->path == NULL) { - perror(path); - goto fail_free; - } - - file->hnd = CreateFileW(wpath, GENERIC_READ, FILE_SHARE_READ, NULL, - OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); - - if (file->hnd == INVALID_HANDLE_VALUE) { - perror(path); - goto fail_path; - } - - free(wpath); - - strm->buffer = file->buffer; - strm->precache = file_precache; - strm->get_filename = file_get_filename; - return strm; -fail_path: - free(file->path); -fail_free: - free(wpath); - free(file); - return NULL; -} - -istream_t *istream_open_stdin(void) -{ - file_istream_t *file = calloc(1, sizeof(*file)); - istream_t *strm = (istream_t *)file; - - if (file == NULL) { - perror("stdin"); - return NULL; - } - - sqfs_object_init(file, file_destroy, NULL); - - strm->buffer = file->buffer; - strm->precache = file_precache; - strm->get_filename = file_get_filename; - return strm; -} diff --git a/lib/io/win32/ostream.c b/lib/io/win32/ostream.c deleted file mode 100644 index 0fe04f3..0000000 --- a/lib/io/win32/ostream.c +++ /dev/null @@ -1,199 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * ostream.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "../internal.h" - -#define WIN32_LEAN_AND_MEAN -#include - -typedef struct { - ostream_t base; - char *path; - HANDLE hnd; -} file_ostream_t; - -static int w32_append(HANDLE hnd, const char *filename, - const void *data, size_t size) -{ - DWORD diff; - - while (size > 0) { - if (!WriteFile(hnd, data, size, &diff, NULL)) { - w32_perror(filename); - return -1; - } - - size -= diff; - data = (const char *)data + diff; - } - - return 0; -} - -static int w32_flush(HANDLE hnd, const char *filename) -{ - if (!FlushFileBuffers(hnd)) { - w32_perror(filename); - return -1; - } - - return 0; -} - -/*****************************************************************************/ - -static int file_append(ostream_t *strm, const void *data, size_t size) -{ - file_ostream_t *file = (file_ostream_t *)strm; - - return w32_append(file->hnd, file->path, data, size); -} - -static int file_append_sparse(ostream_t *strm, size_t size) -{ - file_ostream_t *file = (file_ostream_t *)strm; - LARGE_INTEGER pos; - - pos.QuadPart = size; - - if (!SetFilePointerEx(file->hnd, pos, NULL, FILE_CURRENT)) - goto fail; - - if (!SetEndOfFile(file->hnd)) - goto fail; - - return 0; -fail: - w32_perror(file->path); - return -1; -} - -static int file_flush(ostream_t *strm) -{ - file_ostream_t *file = (file_ostream_t *)strm; - - return w32_flush(file->hnd, file->path); -} - -static void file_destroy(sqfs_object_t *obj) -{ - file_ostream_t *file = (file_ostream_t *)obj; - - CloseHandle(file->hnd); - free(file->path); - free(file); -} - -static const char *file_get_filename(ostream_t *strm) -{ - file_ostream_t *file = (file_ostream_t *)strm; - - return file->path; -} - -/*****************************************************************************/ - -static int stdout_append(ostream_t *strm, const void *data, size_t size) -{ - (void)strm; - return w32_append(GetStdHandle(STD_OUTPUT_HANDLE), "stdout", - data, size); -} - -static int stdout_flush(ostream_t *strm) -{ - (void)strm; - return w32_flush(GetStdHandle(STD_OUTPUT_HANDLE), "stdout"); -} - -static void stdout_destroy(sqfs_object_t *obj) -{ - free(obj); -} - -static const char *stdout_get_filename(ostream_t *strm) -{ - (void)strm; - return "stdout"; -} - -/*****************************************************************************/ - -ostream_t *ostream_open_file(const char *path, int flags) -{ - file_ostream_t *file = calloc(1, sizeof(*file)); - sqfs_object_t *obj = (sqfs_object_t *)file; - ostream_t *strm = (ostream_t *)file; - int access_flags, creation_mode; - WCHAR *wpath = NULL; - - if (file == NULL) { - perror(path); - return NULL; - } - - sqfs_object_init(file, file_destroy, NULL); - - wpath = path_to_windows(path); - if (wpath == NULL) - goto fail_free; - - file->path = strdup(path); - if (file->path == NULL) { - perror(path); - goto fail_free; - } - - access_flags = GENERIC_WRITE; - - if (flags & OSTREAM_OPEN_OVERWRITE) { - creation_mode = CREATE_ALWAYS; - } else { - creation_mode = CREATE_NEW; - } - - file->hnd = CreateFileW(wpath, access_flags, 0, NULL, creation_mode, - FILE_ATTRIBUTE_NORMAL, NULL); - - if (file->hnd == INVALID_HANDLE_VALUE) { - w32_perror(path); - goto fail_path; - } - - free(wpath); - - if (flags & OSTREAM_OPEN_SPARSE) - strm->append_sparse = file_append_sparse; - - strm->append = file_append; - strm->flush = file_flush; - strm->get_filename = file_get_filename; - return strm; -fail_path: - free(file->path); -fail_free: - free(file); - free(wpath); - return NULL; -} - -ostream_t *ostream_open_stdout(void) -{ - ostream_t *strm = calloc(1, sizeof(*strm)); - sqfs_object_t *obj = (sqfs_object_t *)strm; - - if (strm == NULL) { - perror("creating stdout file wrapper"); - return NULL; - } - - sqfs_object_init(strm, stdout_destroy, NULL); - - strm->append = stdout_append; - strm->flush = stdout_flush; - strm->get_filename = stdout_get_filename; - return strm; -} diff --git a/lib/io/xfrm/istream.c b/lib/io/xfrm/istream.c deleted file mode 100644 index 4a1ad77..0000000 --- a/lib/io/xfrm/istream.c +++ /dev/null @@ -1,106 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * istream.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "../internal.h" - -typedef struct istream_xfrm_t { - istream_t base; - - istream_t *wrapped; - xfrm_stream_t *xfrm; - - sqfs_u8 uncompressed[BUFSZ]; -} istream_xfrm_t; - -static int xfrm_precache(istream_t *base) -{ - istream_xfrm_t *xfrm = (istream_xfrm_t *)base; - int ret; - - ret = istream_precache(xfrm->wrapped); - if (ret != 0) - return ret; - - for (;;) { - const sqfs_u32 in_sz = xfrm->wrapped->buffer_used; - const sqfs_u32 out_sz = sizeof(xfrm->uncompressed); - sqfs_u32 in_off = 0, out_off = base->buffer_used; - int mode = XFRM_STREAM_FLUSH_NONE; - - if (xfrm->wrapped->eof) - mode = XFRM_STREAM_FLUSH_FULL; - - ret = xfrm->xfrm->process_data(xfrm->xfrm, - xfrm->wrapped->buffer, in_sz, - base->buffer + out_off, - out_sz - out_off, - &in_off, &out_off, mode); - - if (ret == XFRM_STREAM_ERROR) { - fprintf(stderr, "%s: internal error in decompressor.\n", - base->get_filename(base)); - return -1; - } - - base->buffer_used = out_off; - xfrm->wrapped->buffer_offset = in_off; - - if (ret == XFRM_STREAM_BUFFER_FULL || out_off >= out_sz) - break; - - ret = istream_precache(xfrm->wrapped); - if (ret != 0) - return ret; - - if (xfrm->wrapped->eof && xfrm->wrapped->buffer_used == 0) { - if (base->buffer_used == 0) - base->eof = true; - break; - } - } - - return 0; -} - -static const char *xfrm_get_filename(istream_t *strm) -{ - istream_xfrm_t *xfrm = (istream_xfrm_t *)strm; - - return xfrm->wrapped->get_filename(xfrm->wrapped); -} - -static void xfrm_destroy(sqfs_object_t *obj) -{ - istream_xfrm_t *xfrm = (istream_xfrm_t *)obj; - - sqfs_drop(xfrm->xfrm); - sqfs_drop(xfrm->wrapped); - free(xfrm); -} - -istream_t *istream_xfrm_create(istream_t *strm, xfrm_stream_t *xfrm) -{ - istream_xfrm_t *stream = calloc(1, sizeof(*stream)); - istream_t *base = (istream_t *)stream; - - if (stream == NULL) - goto fail; - - sqfs_object_init(stream, xfrm_destroy, NULL); - - stream->wrapped = sqfs_grab(strm); - stream->xfrm = sqfs_grab(xfrm); - - base->precache = xfrm_precache; - base->get_filename = xfrm_get_filename; - base->buffer = stream->uncompressed; - base->eof = false; - return base; -fail: - fprintf(stderr, "%s: error initializing decompressor stream.\n", - strm->get_filename(strm)); - return NULL; -} diff --git a/lib/io/xfrm/ostream.c b/lib/io/xfrm/ostream.c deleted file mode 100644 index bd94515..0000000 --- a/lib/io/xfrm/ostream.c +++ /dev/null @@ -1,144 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * ostream.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "../internal.h" - -typedef struct ostream_xfrm_t { - ostream_t base; - - ostream_t *wrapped; - xfrm_stream_t *xfrm; - - size_t inbuf_used; - - sqfs_u8 inbuf[BUFSZ]; - sqfs_u8 outbuf[BUFSZ]; -} ostream_xfrm_t; - -static int flush_inbuf(ostream_xfrm_t *xfrm, bool finish) -{ - const sqfs_u32 avail_out = sizeof(xfrm->outbuf); - const sqfs_u32 avail_in = xfrm->inbuf_used; - const int mode = finish ? XFRM_STREAM_FLUSH_FULL : - XFRM_STREAM_FLUSH_NONE; - sqfs_u32 off_in = 0, off_out = 0; - int ret; - - while (finish || off_in < avail_in) { - ret = xfrm->xfrm->process_data(xfrm->xfrm, - xfrm->inbuf + off_in, - avail_in - off_in, - xfrm->outbuf + off_out, - avail_out - off_out, - &off_in, &off_out, mode); - - if (ret == XFRM_STREAM_ERROR) { - fprintf(stderr, - "%s: internal error in compressor.\n", - xfrm->wrapped->get_filename(xfrm->wrapped)); - return -1; - } - - if (ostream_append(xfrm->wrapped, xfrm->outbuf, off_out)) - return -1; - - off_out = 0; - - if (ret == XFRM_STREAM_END) - break; - } - - if (off_out > 0) { - if (ostream_append(xfrm->wrapped, xfrm->outbuf, off_out)) - return -1; - } - - if (off_in < avail_in) { - memmove(xfrm->inbuf, xfrm->inbuf + off_in, avail_in - off_in); - xfrm->inbuf_used -= off_in; - } else { - xfrm->inbuf_used = 0; - } - - return 0; -} - -static int xfrm_append(ostream_t *strm, const void *data, size_t size) -{ - ostream_xfrm_t *xfrm = (ostream_xfrm_t *)strm; - size_t diff; - - while (size > 0) { - if (xfrm->inbuf_used >= BUFSZ) { - if (flush_inbuf(xfrm, false)) - return -1; - } - - diff = BUFSZ - xfrm->inbuf_used; - - if (diff > size) - diff = size; - - memcpy(xfrm->inbuf + xfrm->inbuf_used, data, diff); - - xfrm->inbuf_used += diff; - data = (const char *)data + diff; - size -= diff; - } - - return 0; -} - -static int xfrm_flush(ostream_t *strm) -{ - ostream_xfrm_t *xfrm = (ostream_xfrm_t *)strm; - - if (xfrm->inbuf_used > 0) { - if (flush_inbuf(xfrm, true)) - return -1; - } - - return xfrm->wrapped->flush(xfrm->wrapped); -} - -static const char *xfrm_get_filename(ostream_t *strm) -{ - ostream_xfrm_t *xfrm = (ostream_xfrm_t *)strm; - - return xfrm->wrapped->get_filename(xfrm->wrapped); -} - -static void xfrm_destroy(sqfs_object_t *obj) -{ - ostream_xfrm_t *xfrm = (ostream_xfrm_t *)obj; - - sqfs_drop(xfrm->wrapped); - sqfs_drop(xfrm->xfrm); - free(xfrm); -} - -ostream_t *ostream_xfrm_create(ostream_t *strm, xfrm_stream_t *xfrm) -{ - ostream_xfrm_t *stream = calloc(1, sizeof(*stream)); - ostream_t *base = (ostream_t *)stream; - - if (stream == NULL) - goto fail; - - sqfs_object_init(stream, xfrm_destroy, NULL); - - stream->wrapped = sqfs_grab(strm); - stream->xfrm = sqfs_grab(xfrm); - stream->inbuf_used = 0; - base->append = xfrm_append; - base->flush = xfrm_flush; - base->get_filename = xfrm_get_filename; - return base; -fail: - fprintf(stderr, "%s: error initializing compressor.\n", - strm->get_filename(strm)); - return NULL; -} diff --git a/lib/sqfs/Makemodule.am b/lib/sqfs/Makemodule.am index 748794a..dfd88c9 100644 --- a/lib/sqfs/Makemodule.am +++ b/lib/sqfs/Makemodule.am @@ -10,30 +10,27 @@ LIBSQFS_HEARDS = include/sqfs/meta_writer.h \ include/sqfs/xattr_reader.h include/sqfs/xattr_writer.h \ include/sqfs/frag_table.h include/sqfs/block_writer.h -libsquashfs_la_SOURCES = $(LIBSQFS_HEARDS) lib/sqfs/id_table.c lib/sqfs/super.c -libsquashfs_la_SOURCES += lib/sqfs/readdir.c lib/sqfs/xattr/xattr.c -libsquashfs_la_SOURCES += lib/sqfs/write_table.c lib/sqfs/meta_writer.c -libsquashfs_la_SOURCES += lib/sqfs/read_super.c lib/sqfs/meta_reader.c -libsquashfs_la_SOURCES += lib/sqfs/read_inode.c lib/sqfs/write_inode.c -libsquashfs_la_SOURCES += lib/sqfs/dir_writer.c lib/sqfs/xattr/xattr_reader.c -libsquashfs_la_SOURCES += lib/sqfs/read_table.c lib/sqfs/comp/compressor.c -libsquashfs_la_SOURCES += lib/sqfs/comp/internal.h -libsquashfs_la_SOURCES += lib/sqfs/dir_reader/dir_reader.c -libsquashfs_la_SOURCES += lib/sqfs/dir_reader/read_tree.c -libsquashfs_la_SOURCES += lib/sqfs/dir_reader/get_path.c -libsquashfs_la_SOURCES += lib/sqfs/dir_reader/internal.h -libsquashfs_la_SOURCES += lib/sqfs/inode.c lib/sqfs/xattr/xattr_writer.c -libsquashfs_la_SOURCES += lib/sqfs/xattr/xattr_writer_flush.c -libsquashfs_la_SOURCES += lib/sqfs/xattr/xattr_writer_record.c -libsquashfs_la_SOURCES += lib/sqfs/xattr/xattr_writer.h -libsquashfs_la_SOURCES += lib/sqfs/write_super.c lib/sqfs/data_reader.c -libsquashfs_la_SOURCES += lib/sqfs/block_processor/internal.h -libsquashfs_la_SOURCES += lib/sqfs/block_processor/frontend.c -libsquashfs_la_SOURCES += lib/sqfs/block_processor/block_processor.c -libsquashfs_la_SOURCES += lib/sqfs/block_processor/backend.c -libsquashfs_la_SOURCES += lib/sqfs/frag_table.c include/sqfs/frag_table.h -libsquashfs_la_SOURCES += lib/sqfs/block_writer.c include/sqfs/block_writer.h -libsquashfs_la_SOURCES += lib/sqfs/misc.c +libsquashfs_la_SOURCES = $(LIBSQFS_HEARDS) lib/sqfs/src/id_table.c \ + lib/sqfs/src/super.c lib/sqfs/src/readdir.c lib/sqfs/src/xattr/xattr.c \ + lib/sqfs/src/write_table.c lib/sqfs/src/meta_writer.c \ + lib/sqfs/src/read_super.c lib/sqfs/src/meta_reader.c \ + lib/sqfs/src/read_inode.c lib/sqfs/src/write_inode.c \ + lib/sqfs/src/dir_writer.c lib/sqfs/src/xattr/xattr_reader.c \ + lib/sqfs/src/read_table.c lib/sqfs/src/comp/compressor.c \ + lib/sqfs/src/comp/internal.h lib/sqfs/src/dir_reader/dir_reader.c \ + lib/sqfs/src/dir_reader/read_tree.c lib/sqfs/src/dir_reader/get_path.c \ + lib/sqfs/src/dir_reader/internal.h lib/sqfs/src/inode.c \ + lib/sqfs/src/xattr/xattr_writer.c \ + lib/sqfs/src/xattr/xattr_writer_flush.c \ + lib/sqfs/src/xattr/xattr_writer_record.c \ + lib/sqfs/src/xattr/xattr_writer.h \ + lib/sqfs/src/write_super.c lib/sqfs/src/data_reader.c \ + lib/sqfs/src/block_processor/internal.h \ + lib/sqfs/src/block_processor/frontend.c \ + lib/sqfs/src/block_processor/block_processor.c \ + lib/sqfs/src/block_processor/backend.c \ + lib/sqfs/src/frag_table.c lib/sqfs/src/block_writer.c \ + lib/sqfs/src/misc.c libsquashfs_la_CPPFLAGS = $(AM_CPPFLAGS) libsquashfs_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(LIBSQUASHFS_SO_VERSION) libsquashfs_la_CFLAGS = $(AM_CFLAGS) $(PTHREAD_CFLAGS) $(ZLIB_CFLAGS) @@ -43,60 +40,60 @@ libsquashfs_la_LIBADD = $(XZ_LIBS) $(ZLIB_LIBS) $(LZ4_LIBS) libsquashfs_la_LIBADD += $(ZSTD_LIBS) $(PTHREAD_LIBS) # directly "import" stuff from libutil -libsquashfs_la_SOURCES += lib/util/str_table.c lib/util/alloc.c -libsquashfs_la_SOURCES += lib/util/xxhash.c lib/util/file_cmp.c -libsquashfs_la_SOURCES += lib/util/hash_table.c include/util/hash_table.h -libsquashfs_la_SOURCES += lib/util/rbtree.c include/util/rbtree.h -libsquashfs_la_SOURCES += lib/util/array.c include/util/array.h -libsquashfs_la_SOURCES += lib/util/is_memory_zero.c +libsquashfs_la_SOURCES += lib/util/src/str_table.c lib/util/src/alloc.c +libsquashfs_la_SOURCES += lib/util/src/xxhash.c lib/util/src/file_cmp.c +libsquashfs_la_SOURCES += lib/util/src/hash_table.c include/util/hash_table.h +libsquashfs_la_SOURCES += lib/util/src/rbtree.c include/util/rbtree.h +libsquashfs_la_SOURCES += lib/util/src/array.c include/util/array.h +libsquashfs_la_SOURCES += lib/util/src/is_memory_zero.c libsquashfs_la_SOURCES += include/util/threadpool.h # and from libcompat -libsquashfs_la_SOURCES += lib/compat/strchrnul.c lib/compat/strndup.c +libsquashfs_la_SOURCES += lib/compat/src/strchrnul.c lib/compat/src/strndup.c libsquashfs_la_SOURCES += include/compat.h if CUSTOM_ALLOC -libsquashfs_la_SOURCES += lib/util/mempool.c include/util/mempool.h +libsquashfs_la_SOURCES += lib/util/src/mempool.c include/util/mempool.h endif if WINDOWS -libsquashfs_la_SOURCES += lib/sqfs/win32/io_file.c +libsquashfs_la_SOURCES += lib/sqfs/src/win32/io_file.c libsquashfs_la_CFLAGS += -DWINVER=0x0600 -D_WIN32_WINNT=0x0600 libsquashfs_la_CFLAGS += -Wc,-static-libgcc libsquashfs_la_LDFLAGS += -no-undefined -avoid-version else -libsquashfs_la_SOURCES += lib/sqfs/unix/io_file.c +libsquashfs_la_SOURCES += lib/sqfs/src/unix/io_file.c endif if HAVE_PTHREAD -libsquashfs_la_SOURCES += lib/util/threadpool.c +libsquashfs_la_SOURCES += lib/util/src/threadpool.c else if WINDOWS -libsquashfs_la_SOURCES += lib/util/threadpool.c +libsquashfs_la_SOURCES += lib/util/src/threadpool.c else -libsquashfs_la_SOURCES += lib/util/threadpool_serial.c +libsquashfs_la_SOURCES += lib/util/src/threadpool_serial.c libsquashfs_la_CPPFLAGS += -DNO_THREAD_IMPL endif endif if WITH_GZIP -libsquashfs_la_SOURCES += lib/sqfs/comp/gzip.c +libsquashfs_la_SOURCES += lib/sqfs/src/comp/gzip.c libsquashfs_la_CPPFLAGS += -DWITH_GZIP endif if WITH_XZ -libsquashfs_la_SOURCES += lib/sqfs/comp/xz.c -libsquashfs_la_SOURCES += lib/sqfs/comp/lzma.c +libsquashfs_la_SOURCES += lib/sqfs/src/comp/xz.c +libsquashfs_la_SOURCES += lib/sqfs/src/comp/lzma.c libsquashfs_la_CPPFLAGS += -DWITH_XZ endif if WITH_LZ4 -libsquashfs_la_SOURCES += lib/sqfs/comp/lz4.c +libsquashfs_la_SOURCES += lib/sqfs/src/comp/lz4.c libsquashfs_la_CPPFLAGS += -DWITH_LZ4 endif if WITH_ZSTD -libsquashfs_la_SOURCES += lib/sqfs/comp/zstd.c +libsquashfs_la_SOURCES += lib/sqfs/src/comp/zstd.c libsquashfs_la_CPPFLAGS += -DWITH_ZSTD endif diff --git a/lib/sqfs/block_processor/backend.c b/lib/sqfs/block_processor/backend.c deleted file mode 100644 index b443c9d..0000000 --- a/lib/sqfs/block_processor/backend.c +++ /dev/null @@ -1,335 +0,0 @@ -/* SPDX-License-Identifier: LGPL-3.0-or-later */ -/* - * backend.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#define SQFS_BUILDING_DLL -#include "internal.h" - -static int set_block_size(sqfs_inode_generic_t **inode, - sqfs_u32 index, sqfs_u32 size) -{ - size_t min_size = (index + 1) * sizeof(sqfs_u32); - size_t avail = (*inode)->payload_bytes_available; - sqfs_inode_generic_t *new; - size_t newsz; - - if (avail < min_size) { - newsz = avail ? avail : (sizeof(sqfs_u32) * 4); - while (newsz < min_size) - newsz *= 2; - - if (SZ_ADD_OV(newsz, sizeof(**inode), &newsz)) - return SQFS_ERROR_OVERFLOW; - - if (sizeof(size_t) > sizeof(sqfs_u32)) { - if ((newsz - sizeof(**inode)) > 0x0FFFFFFFFUL) - return SQFS_ERROR_OVERFLOW; - } - - new = realloc((*inode), newsz); - if (new == NULL) - return SQFS_ERROR_ALLOC; - - (*inode) = new; - (*inode)->payload_bytes_available = newsz - sizeof(**inode); - } - - (*inode)->extra[index] = size; - - if (min_size >= (*inode)->payload_bytes_used) - (*inode)->payload_bytes_used = min_size; - - return 0; -} - -static void release_old_block(sqfs_block_processor_t *proc, sqfs_block_t *blk) -{ - blk->next = proc->free_list; - proc->free_list = blk; - - proc->backlog -= 1; -} - -static int process_completed_block(sqfs_block_processor_t *proc, sqfs_block_t *blk) -{ - sqfs_u64 location; - sqfs_u32 size; - int err; - - if (blk->flags & SQFS_BLK_FRAGMENT_BLOCK) { - sqfs_block_t *it = proc->fblk_in_flight, *prev = NULL; - - while (it != NULL && it->index != blk->index) { - prev = it; - it = it->next; - } - - if (it != NULL) { - if (prev == NULL) { - proc->fblk_in_flight = it->next; - } else { - prev->next = it->next; - } - free(it); - } - } - - err = proc->wr->write_data_block(proc->wr, blk->user, blk->size, - blk->checksum, - blk->flags & ~BLK_FLAG_INTERNAL, - blk->data, &location); - if (err) - goto out; - - proc->stats.output_bytes_generated += blk->size; - - if (blk->flags & SQFS_BLK_IS_SPARSE) { - if (blk->inode != NULL) { - sqfs_inode_make_extended(*(blk->inode)); - (*(blk->inode))->data.file_ext.sparse += blk->size; - - err = set_block_size(blk->inode, blk->index, 0); - if (err) - goto out; - } - proc->stats.sparse_block_count += 1; - } else if (blk->size != 0) { - size = blk->size; - if (!(blk->flags & SQFS_BLK_IS_COMPRESSED)) - size |= 1 << 24; - - if (blk->flags & SQFS_BLK_FRAGMENT_BLOCK) { - if (proc->frag_tbl != NULL) { - err = sqfs_frag_table_set(proc->frag_tbl, - blk->index, location, - size); - if (err) - goto out; - } - proc->stats.frag_block_count += 1; - } else { - if (blk->inode != NULL) { - err = set_block_size(blk->inode, blk->index, - size); - if (err) - goto out; - } - proc->stats.data_block_count += 1; - } - } - - if (blk->flags & SQFS_BLK_LAST_BLOCK && blk->inode != NULL) - sqfs_inode_set_file_block_start(*(blk->inode), location); -out: - release_old_block(proc, blk); - return err; -} - -static int process_completed_fragment(sqfs_block_processor_t *proc, - sqfs_block_t *frag) -{ - chunk_info_t *chunk = NULL, search; - struct hash_entry *entry; - sqfs_u32 index, offset; - int err; - - if (frag->flags & SQFS_BLK_IS_SPARSE) { - if (frag->inode != NULL) { - sqfs_inode_make_extended(*(frag->inode)); - set_block_size(frag->inode, frag->index, 0); - (*(frag->inode))->data.file_ext.sparse += frag->size; - } - proc->stats.sparse_block_count += 1; - release_old_block(proc, frag); - return 0; - } - - proc->stats.total_frag_count += 1; - - if (!(frag->flags & SQFS_BLK_DONT_DEDUPLICATE)) { - search.hash = frag->checksum; - search.size = frag->size; - - proc->current_frag = frag; - proc->fblk_lookup_error = 0; - entry = hash_table_search_pre_hashed(proc->frag_ht, - search.hash, &search); - proc->current_frag = NULL; - - if (proc->fblk_lookup_error != 0) { - err = proc->fblk_lookup_error; - goto fail; - } - - if (entry != NULL) { - if (frag->inode != NULL) { - chunk = entry->data; - sqfs_inode_set_frag_location(*(frag->inode), - chunk->index, - chunk->offset); - } - release_old_block(proc, frag); - return 0; - } - } - - if (proc->frag_block != NULL) { - size_t size = proc->frag_block->size + frag->size; - - if (size > proc->max_block_size) { - proc->frag_block->io_seq_num = proc->io_seq_num++; - - err = enqueue_block(proc, proc->frag_block); - proc->frag_block = NULL; - - if (err) - goto fail; - } - } - - if (proc->frag_block == NULL) { - if (proc->frag_tbl == NULL) { - index = 0; - } else { - err = sqfs_frag_table_append(proc->frag_tbl, - 0, 0, &index); - if (err) - goto fail; - } - - offset = 0; - proc->frag_block = frag; - proc->frag_block->index = index; - proc->frag_block->flags &= - (SQFS_BLK_DONT_COMPRESS | SQFS_BLK_ALIGN); - proc->frag_block->flags |= SQFS_BLK_FRAGMENT_BLOCK; - } else { - index = proc->frag_block->index; - offset = proc->frag_block->size; - - memcpy(proc->frag_block->data + proc->frag_block->size, - frag->data, frag->size); - - proc->frag_block->size += frag->size; - proc->frag_block->flags |= - (frag->flags & - (SQFS_BLK_DONT_COMPRESS | SQFS_BLK_ALIGN)); - } - - if (proc->frag_tbl != NULL) { - err = SQFS_ERROR_ALLOC; - chunk = calloc(1, sizeof(*chunk)); - if (chunk == NULL) - goto fail; - - chunk->index = index; - chunk->offset = offset; - chunk->size = frag->size; - chunk->hash = frag->checksum; - - proc->current_frag = frag; - proc->fblk_lookup_error = 0; - entry = hash_table_insert_pre_hashed(proc->frag_ht, chunk->hash, - chunk, chunk); - proc->current_frag = NULL; - - if (proc->fblk_lookup_error != 0) { - err = proc->fblk_lookup_error; - goto fail; - } - - if (entry == NULL) - goto fail; - } - - if (frag->inode != NULL) - sqfs_inode_set_frag_location(*(frag->inode), index, offset); - - if (frag != proc->frag_block) - release_old_block(proc, frag); - - proc->stats.actual_frag_count += 1; - return 0; -fail: - free(chunk); - if (frag != proc->frag_block) - release_old_block(proc, frag); - return err; -} - -static void store_io_block(sqfs_block_processor_t *proc, sqfs_block_t *blk) -{ - sqfs_block_t *prev = NULL, *it = proc->io_queue; - - while (it != NULL && (it->io_seq_num < blk->io_seq_num)) { - prev = it; - it = it->next; - } - - if (prev == NULL) { - proc->io_queue = blk; - } else { - prev->next = blk; - } - - blk->next = it; -} - -int dequeue_block(sqfs_block_processor_t *proc) -{ - size_t backlog_old = proc->backlog; - sqfs_block_t *blk; - int status; - - do { - while (proc->io_queue != NULL) { - if (proc->io_queue->io_seq_num != proc->io_deq_seq_num) - break; - - blk = proc->io_queue; - proc->io_queue = blk->next; - proc->io_deq_seq_num += 1; - - status = process_completed_block(proc, blk); - if (status != 0) - return status; - } - - if (proc->backlog < backlog_old) - break; - - if ((proc->backlog == 1) && - (proc->frag_block != NULL || proc->blk_current != NULL)) { - break; - } - - if ((proc->backlog == 2) && - proc->frag_block != NULL && proc->blk_current != NULL) { - break; - } - - blk = proc->pool->dequeue(proc->pool); - - if (blk == NULL) { - status = proc->pool->get_status(proc->pool); - return status ? status : SQFS_ERROR_INTERNAL; - } - - if (blk->flags & SQFS_BLK_IS_FRAGMENT) { - status = process_completed_fragment(proc, blk); - if (status != 0) - return status; - } else { - if (!(blk->flags & SQFS_BLK_FRAGMENT_BLOCK) || - (blk->flags & BLK_FLAG_MANUAL_SUBMISSION)) { - blk->io_seq_num = proc->io_seq_num++; - } - - store_io_block(proc, blk); - } - } while (proc->backlog >= backlog_old); - - return 0; -} diff --git a/lib/sqfs/block_processor/block_processor.c b/lib/sqfs/block_processor/block_processor.c deleted file mode 100644 index d607437..0000000 --- a/lib/sqfs/block_processor/block_processor.c +++ /dev/null @@ -1,358 +0,0 @@ -/* SPDX-License-Identifier: LGPL-3.0-or-later */ -/* - * block_processor.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#define SQFS_BUILDING_DLL -#include "internal.h" - -static int process_block(void *userptr, void *workitem) -{ - worker_data_t *worker = userptr; - sqfs_block_t *block = workitem; - sqfs_s32 ret; - - if (block->size == 0) - return 0; - - if (!(block->flags & SQFS_BLK_IGNORE_SPARSE) && - is_memory_zero(block->data, block->size)) { - block->flags |= SQFS_BLK_IS_SPARSE; - return 0; - } - - if (block->flags & SQFS_BLK_DONT_HASH) { - block->checksum = 0; - } else { - block->checksum = xxh32(block->data, block->size); - } - - if (block->flags & (SQFS_BLK_IS_FRAGMENT | SQFS_BLK_DONT_COMPRESS)) - return 0; - - ret = worker->cmp->do_block(worker->cmp, block->data, block->size, - worker->scratch, worker->scratch_size); - if (ret < 0) - return ret; - - if (ret > 0) { - memcpy(block->data, worker->scratch, ret); - block->size = ret; - block->flags |= SQFS_BLK_IS_COMPRESSED; - } - return 0; -} - -static int load_frag_block(sqfs_block_processor_t *proc, sqfs_u32 index) -{ - sqfs_fragment_t info; - size_t size; - int ret; - - if (proc->cached_frag_blk == NULL) { - size = sizeof(*proc->cached_frag_blk); - - proc->cached_frag_blk = alloc_flex(size, 1, - proc->max_block_size); - - if (proc->cached_frag_blk == NULL) - return SQFS_ERROR_ALLOC; - } else { - if (proc->cached_frag_blk->index == index) - return 0; - } - - ret = sqfs_frag_table_lookup(proc->frag_tbl, index, &info); - if (ret != 0) - return ret; - - size = SQFS_ON_DISK_BLOCK_SIZE(info.size); - if (size > proc->max_block_size) - return SQFS_ERROR_CORRUPTED; - - if (SQFS_IS_BLOCK_COMPRESSED(info.size)) { - ret = proc->file->read_at(proc->file, info.start_offset, - proc->scratch, size); - if (ret != 0) - return ret; - - ret = proc->uncmp->do_block(proc->uncmp, proc->scratch, size, - proc->cached_frag_blk->data, - proc->max_block_size); - if (ret <= 0) - return ret ? ret : SQFS_ERROR_OVERFLOW; - - size = ret; - } else { - ret = proc->file->read_at(proc->file, info.start_offset, - proc->cached_frag_blk->data, size); - if (ret != 0) - return ret; - } - - proc->cached_frag_blk->size = size; - proc->cached_frag_blk->index = index; - return 0; -} - -static bool chunk_info_equals(void *user, const void *k, const void *c) -{ - const chunk_info_t *key = k, *cmp = c; - sqfs_block_processor_t *proc = user; - sqfs_block_t *it; - int ret; - - if (key->size != cmp->size || key->hash != cmp->hash) - return false; - - if (proc->uncmp == NULL || proc->file == NULL) - return true; - - if (proc->current_frag == NULL || proc->frag_tbl == NULL) - return true; - - if (proc->fblk_lookup_error != 0) - return false; - - for (it = proc->fblk_in_flight; it != NULL; it = it->next) { - if (it->index == cmp->index) - break; - } - - if (it == NULL && proc->frag_block != NULL) { - if (proc->frag_block->index == cmp->index) - it = proc->frag_block; - } - - if (it == NULL) { - ret = load_frag_block(proc, cmp->index); - if (ret != 0) { - proc->fblk_lookup_error = ret; - return false; - } - - it = proc->cached_frag_blk; - } - - if (cmp->offset >= it->size || (it->size - cmp->offset) < cmp->size) { - proc->fblk_lookup_error = SQFS_ERROR_CORRUPTED; - return false; - } - - if (cmp->size != proc->current_frag->size) { - proc->fblk_lookup_error = SQFS_ERROR_CORRUPTED; - return false; - } - - return memcmp(it->data + cmp->offset, - proc->current_frag->data, cmp->size) == 0; -} - -static void ht_delete_function(struct hash_entry *entry) -{ - free(entry->data); -} - -static void free_block_list(sqfs_block_t *list) -{ - while (list != NULL) { - sqfs_block_t *it = list; - list = it->next; - free(it); - } -} - -static void block_processor_destroy(sqfs_object_t *base) -{ - sqfs_block_processor_t *proc = (sqfs_block_processor_t *)base; - - free(proc->frag_block); - free(proc->blk_current); - free(proc->cached_frag_blk); - - free_block_list(proc->free_list); - free_block_list(proc->io_queue); - free_block_list(proc->fblk_in_flight); - - if (proc->frag_ht != NULL) - hash_table_destroy(proc->frag_ht, ht_delete_function); - - /* XXX: shut down the pool first before cleaning up the worker data */ - if (proc->pool != NULL) - proc->pool->destroy(proc->pool); - - while (proc->workers != NULL) { - worker_data_t *worker = proc->workers; - proc->workers = worker->next; - - sqfs_drop(worker->cmp); - free(worker); - } - - sqfs_drop(proc->frag_tbl); - sqfs_drop(proc->wr); - sqfs_drop(proc->file); - sqfs_drop(proc->uncmp); - free(proc); -} - -int sqfs_block_processor_sync(sqfs_block_processor_t *proc) -{ - int ret; - - for (;;) { - if (proc->backlog == 0) - break; - - if ((proc->backlog == 1) && - (proc->frag_block != NULL || proc->blk_current != NULL)) { - break; - } - - if ((proc->backlog == 2) && - proc->frag_block != NULL && proc->blk_current != NULL) { - break; - } - - ret = dequeue_block(proc); - if (ret != 0) - return ret; - } - - return 0; -} - -int sqfs_block_processor_finish(sqfs_block_processor_t *proc) -{ - sqfs_block_t *blk; - int status; - - status = sqfs_block_processor_sync(proc); - if (status != 0) - return status; - - if (proc->frag_block != NULL) { - blk = proc->frag_block; - blk->next = NULL; - proc->frag_block = NULL; - - blk->io_seq_num = proc->io_seq_num++; - - status = enqueue_block(proc, blk); - if (status != 0) - return status; - - status = sqfs_block_processor_sync(proc); - } - - return status; -} - -const sqfs_block_processor_stats_t -*sqfs_block_processor_get_stats(const sqfs_block_processor_t *proc) -{ - return &proc->stats; -} - -int sqfs_block_processor_create_ex(const sqfs_block_processor_desc_t *desc, - sqfs_block_processor_t **out) -{ - size_t i, count, scratch_size = 0; - sqfs_block_processor_t *proc; - int ret; - - if (desc->size != sizeof(sqfs_block_processor_desc_t)) - return SQFS_ERROR_ARG_INVALID; - - if (desc->file != NULL && desc->uncmp != NULL) - scratch_size = desc->max_block_size; - - proc = alloc_flex(sizeof(*proc), 1, scratch_size); - if (proc == NULL) - return SQFS_ERROR_ALLOC; - - sqfs_object_init(proc, block_processor_destroy, NULL); - - proc->max_backlog = desc->max_backlog; - proc->max_block_size = desc->max_block_size; - proc->frag_tbl = sqfs_grab(desc->tbl); - proc->wr = sqfs_grab(desc->wr); - proc->file = sqfs_grab(desc->file); - proc->uncmp = sqfs_grab(desc->uncmp); - proc->stats.size = sizeof(proc->stats); - - /* we need at least one current data block + one fragment block */ - if (proc->max_backlog < 3) - proc->max_backlog = 3; - - /* create the thread pool */ - proc->pool = thread_pool_create(desc->num_workers, process_block); - if (proc->pool == NULL) { - ret = SQFS_ERROR_INTERNAL; - goto fail_pool; - } - - /* create the worker compressors & scratch buffer */ - count = proc->pool->get_worker_count(proc->pool); - - for (i = 0; i < count; ++i) { - worker_data_t *worker = alloc_flex(sizeof(*worker), 1, - desc->max_block_size); - if (worker == NULL) { - ret = SQFS_ERROR_ALLOC; - goto fail_pool; - } - - worker->scratch_size = desc->max_block_size; - worker->next = proc->workers; - proc->workers = worker; - - worker->cmp = sqfs_copy(desc->cmp); - if (worker->cmp == NULL) { - ret = SQFS_ERROR_ALLOC; - goto fail_pool; - } - - proc->pool->set_worker_ptr(proc->pool, i, worker); - } - - /* create the fragment hash table */ - proc->frag_ht = hash_table_create(NULL, chunk_info_equals); - if (proc->frag_ht == NULL) { - ret = SQFS_ERROR_ALLOC; - goto fail_pool; - } - - proc->frag_ht->user = proc; - *out = proc; - return 0; -fail_pool: - block_processor_destroy((sqfs_object_t *)proc); - return ret; -} - -sqfs_block_processor_t *sqfs_block_processor_create(size_t max_block_size, - sqfs_compressor_t *cmp, - unsigned int num_workers, - size_t max_backlog, - sqfs_block_writer_t *wr, - sqfs_frag_table_t *tbl) -{ - sqfs_block_processor_desc_t desc; - sqfs_block_processor_t *out; - - memset(&desc, 0, sizeof(desc)); - desc.size = sizeof(desc); - desc.max_block_size = max_block_size; - desc.num_workers = num_workers; - desc.max_backlog = max_backlog; - desc.cmp = cmp; - desc.wr = wr; - desc.tbl = tbl; - - if (sqfs_block_processor_create_ex(&desc, &out) != 0) - return NULL; - - return out; -} diff --git a/lib/sqfs/block_processor/frontend.c b/lib/sqfs/block_processor/frontend.c deleted file mode 100644 index e8a4207..0000000 --- a/lib/sqfs/block_processor/frontend.c +++ /dev/null @@ -1,243 +0,0 @@ -/* SPDX-License-Identifier: LGPL-3.0-or-later */ -/* - * frontend.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#define SQFS_BUILDING_DLL -#include "internal.h" - -static int get_new_block(sqfs_block_processor_t *proc, sqfs_block_t **out) -{ - sqfs_block_t *blk; - - while (proc->backlog >= proc->max_backlog) { - int ret = dequeue_block(proc); - if (ret != 0) - return ret; - } - - if (proc->free_list != NULL) { - blk = proc->free_list; - proc->free_list = blk->next; - } else { - blk = malloc(sizeof(*blk) + proc->max_block_size); - if (blk == NULL) - return SQFS_ERROR_ALLOC; - } - - memset(blk, 0, sizeof(*blk)); - *out = blk; - - proc->backlog += 1; - return 0; -} - -static int add_sentinel_block(sqfs_block_processor_t *proc) -{ - sqfs_block_t *blk; - int ret; - - ret = get_new_block(proc, &blk); - if (ret != 0) - return ret; - - blk->inode = proc->inode; - blk->flags = proc->blk_flags | SQFS_BLK_LAST_BLOCK; - - return enqueue_block(proc, blk); -} - -int enqueue_block(sqfs_block_processor_t *proc, sqfs_block_t *blk) -{ - int status; - - if ((blk->flags & SQFS_BLK_FRAGMENT_BLOCK) && - proc->file != NULL && proc->uncmp != NULL) { - sqfs_block_t *copy = alloc_flex(sizeof(*copy), 1, blk->size); - - if (copy == NULL) - return SQFS_ERROR_ALLOC; - - copy->size = blk->size; - copy->index = blk->index; - memcpy(copy->data, blk->data, blk->size); - - copy->next = proc->fblk_in_flight; - proc->fblk_in_flight = copy; - } - - if (proc->pool->submit(proc->pool, blk) != 0) { - status = proc->pool->get_status(proc->pool); - - if (status == 0) - status = SQFS_ERROR_ALLOC; - - blk->next = proc->free_list; - proc->free_list = blk; - return status; - } - - return 0; -} - -int sqfs_block_processor_begin_file(sqfs_block_processor_t *proc, - sqfs_inode_generic_t **inode, - void *user, sqfs_u32 flags) -{ - if (proc->begin_called) - return SQFS_ERROR_SEQUENCE; - - if (flags & ~SQFS_BLK_USER_SETTABLE_FLAGS) - return SQFS_ERROR_UNSUPPORTED; - - if (inode != NULL) { - (*inode) = calloc(1, sizeof(sqfs_inode_generic_t)); - if ((*inode) == NULL) - return SQFS_ERROR_ALLOC; - - (*inode)->base.type = SQFS_INODE_FILE; - sqfs_inode_set_frag_location(*inode, 0xFFFFFFFF, 0xFFFFFFFF); - } - - proc->begin_called = true; - proc->inode = inode; - proc->blk_flags = flags | SQFS_BLK_FIRST_BLOCK; - proc->blk_index = 0; - proc->user = user; - return 0; -} - -int sqfs_block_processor_append(sqfs_block_processor_t *proc, const void *data, - size_t size) -{ - sqfs_block_t *new; - sqfs_u64 filesize; - size_t diff; - int err; - - if (!proc->begin_called) - return SQFS_ERROR_SEQUENCE; - - if (proc->inode != NULL) { - sqfs_inode_get_file_size(*(proc->inode), &filesize); - sqfs_inode_set_file_size(*(proc->inode), filesize + size); - } - - while (size > 0) { - if (proc->blk_current == NULL) { - err = get_new_block(proc, &new); - if (err != 0) - return err; - - proc->blk_current = new; - proc->blk_current->flags = proc->blk_flags; - proc->blk_current->inode = proc->inode; - proc->blk_current->user = proc->user; - proc->blk_current->index = proc->blk_index++; - proc->blk_flags &= ~SQFS_BLK_FIRST_BLOCK; - } - - diff = proc->max_block_size - proc->blk_current->size; - - if (diff == 0) { - err = enqueue_block(proc, proc->blk_current); - proc->blk_current = NULL; - - if (err) - return err; - continue; - } - - if (diff > size) - diff = size; - - memcpy(proc->blk_current->data + proc->blk_current->size, - data, diff); - - size -= diff; - proc->blk_current->size += diff; - data = (const char *)data + diff; - - proc->stats.input_bytes_read += diff; - } - - if (proc->blk_current->size == proc->max_block_size) { - err = enqueue_block(proc, proc->blk_current); - proc->blk_current = NULL; - - if (err) - return err; - } - - return 0; -} - -int sqfs_block_processor_end_file(sqfs_block_processor_t *proc) -{ - int err; - - if (!proc->begin_called) - return SQFS_ERROR_SEQUENCE; - - if (proc->blk_current == NULL) { - if (!(proc->blk_flags & SQFS_BLK_FIRST_BLOCK)) { - err = add_sentinel_block(proc); - if (err) - return err; - } - } else { - if (proc->blk_flags & SQFS_BLK_DONT_FRAGMENT) { - proc->blk_current->flags |= SQFS_BLK_LAST_BLOCK; - } else { - if (!(proc->blk_current->flags & - SQFS_BLK_FIRST_BLOCK)) { - err = add_sentinel_block(proc); - if (err) - return err; - } - - proc->blk_current->flags |= SQFS_BLK_IS_FRAGMENT; - } - - err = enqueue_block(proc, proc->blk_current); - proc->blk_current = NULL; - - if (err) - return err; - } - - proc->begin_called = false; - proc->inode = NULL; - proc->user = NULL; - proc->blk_flags = 0; - return 0; -} - -int sqfs_block_processor_submit_block(sqfs_block_processor_t *proc, void *user, - sqfs_u32 flags, const void *data, - size_t size) -{ - sqfs_block_t *blk; - int ret; - - if (proc->begin_called) - return SQFS_ERROR_SEQUENCE; - - if (size > proc->max_block_size) - return SQFS_ERROR_OVERFLOW; - - if (flags & ~SQFS_BLK_FLAGS_ALL) - return SQFS_ERROR_UNSUPPORTED; - - ret = get_new_block(proc, &blk); - if (ret != 0) - return ret; - - blk->flags = flags | BLK_FLAG_MANUAL_SUBMISSION; - blk->user = user; - blk->size = size; - memcpy(blk->data, data, size); - - return enqueue_block(proc, blk); -} diff --git a/lib/sqfs/block_processor/internal.h b/lib/sqfs/block_processor/internal.h deleted file mode 100644 index 0b2c88d..0000000 --- a/lib/sqfs/block_processor/internal.h +++ /dev/null @@ -1,115 +0,0 @@ -/* SPDX-License-Identifier: LGPL-3.0-or-later */ -/* - * internal.h - * - * Copyright (C) 2019 David Oberhollenzer - */ -#ifndef INTERNAL_H -#define INTERNAL_H - -#include "config.h" - -#include "sqfs/block_processor.h" -#include "sqfs/block_writer.h" -#include "sqfs/frag_table.h" -#include "sqfs/compressor.h" -#include "sqfs/inode.h" -#include "sqfs/table.h" -#include "sqfs/error.h" -#include "sqfs/block.h" -#include "sqfs/io.h" - -#include "util/hash_table.h" -#include "util/threadpool.h" -#include "util/util.h" - -#include -#include - -typedef struct { - sqfs_u32 index; - sqfs_u32 offset; - sqfs_u32 size; - sqfs_u32 hash; -} chunk_info_t; - -enum { - BLK_FLAG_MANUAL_SUBMISSION = 0x10000000, - BLK_FLAG_INTERNAL = 0x10000000, -}; - -typedef struct sqfs_block_t { - struct sqfs_block_t *next; - sqfs_inode_generic_t **inode; - - sqfs_u32 io_seq_num; - sqfs_u32 flags; - sqfs_u32 size; - sqfs_u32 checksum; - - /* For data blocks: index within the inode. - For fragment fragment blocks: fragment table index. */ - sqfs_u32 index; - - /* User data pointer */ - void *user; - - sqfs_u8 data[]; -} sqfs_block_t; - -typedef struct worker_data_t { - struct worker_data_t *next; - sqfs_compressor_t *cmp; - - size_t scratch_size; - sqfs_u8 scratch[]; -} worker_data_t; - -struct sqfs_block_processor_t { - sqfs_object_t obj; - - sqfs_frag_table_t *frag_tbl; - sqfs_block_t *frag_block; - sqfs_block_writer_t *wr; - - sqfs_block_processor_stats_t stats; - - sqfs_inode_generic_t **inode; - sqfs_block_t *blk_current; - sqfs_u32 blk_flags; - sqfs_u32 blk_index; - void *user; - - struct hash_table *frag_ht; - sqfs_block_t *free_list; - - size_t max_block_size; - size_t max_backlog; - size_t backlog; - - bool begin_called; - - sqfs_file_t *file; - sqfs_compressor_t *uncmp; - - thread_pool_t *pool; - worker_data_t *workers; - - sqfs_block_t *io_queue; - sqfs_u32 io_seq_num; - sqfs_u32 io_deq_seq_num; - - sqfs_block_t *current_frag; - sqfs_block_t *cached_frag_blk; - sqfs_block_t *fblk_in_flight; - int fblk_lookup_error; - - sqfs_u8 scratch[]; -}; - -SQFS_INTERNAL int enqueue_block(sqfs_block_processor_t *proc, - sqfs_block_t *blk); - -SQFS_INTERNAL int dequeue_block(sqfs_block_processor_t *proc); - -#endif /* INTERNAL_H */ diff --git a/lib/sqfs/block_writer.c b/lib/sqfs/block_writer.c deleted file mode 100644 index a5135bc..0000000 --- a/lib/sqfs/block_writer.c +++ /dev/null @@ -1,238 +0,0 @@ -/* SPDX-License-Identifier: LGPL-3.0-or-later */ -/* - * block_writer.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#define SQFS_BUILDING_DLL -#include "config.h" - -#include "sqfs/block_writer.h" -#include "sqfs/error.h" -#include "sqfs/block.h" -#include "sqfs/io.h" - -#include "util/array.h" -#include "util/util.h" - -#include -#include - -#define MK_BLK_HASH(chksum, size) \ - (((sqfs_u64)(size) << 32) | (sqfs_u64)(chksum)) - -#define SIZE_FROM_HASH(hash) ((hash >> 32) & ((1 << 24) - 1)) - -#define INIT_BLOCK_COUNT (128) -#define SCRATCH_SIZE (8192) - -typedef struct { - sqfs_u64 offset; - sqfs_u64 hash; -} blk_info_t; - -typedef struct { - sqfs_block_writer_t base; - sqfs_file_t *file; - - array_t blocks; - size_t devblksz; - - size_t file_start; - - sqfs_u32 flags; - - sqfs_u8 scratch[]; -} block_writer_default_t; - -static int store_block_location(block_writer_default_t *wr, sqfs_u64 offset, - sqfs_u32 size, sqfs_u32 chksum) -{ - blk_info_t info = { offset, MK_BLK_HASH(chksum, size) }; - - return array_append(&(wr->blocks), &info); -} - -static int deduplicate_blocks(block_writer_default_t *wr, sqfs_u32 flags, sqfs_u64 *out) -{ - const blk_info_t *blocks = wr->blocks.data; - sqfs_u64 loc_a, loc_b, sz; - size_t i, j, count; - int ret; - - count = wr->blocks.used - wr->file_start; - if (count == 0) { - *out = 0; - return 0; - } - - if (flags & SQFS_BLK_DONT_DEDUPLICATE) { - *out = blocks[wr->file_start].offset; - return 0; - } - - sz = 0; - loc_a = blocks[wr->file_start].offset; - - for (i = 0; i < count; ++i) - sz += SIZE_FROM_HASH(blocks[wr->file_start + i].hash); - - for (i = 0; i < wr->file_start; ++i) { - for (j = 0; j < count; ++j) { - if (blocks[i + j].hash == 0) - break; - - if (blocks[i + j].hash != - blocks[wr->file_start + j].hash) - break; - } - - if (j != count) - continue; - - if (wr->flags & SQFS_BLOCK_WRITER_HASH_COMPARE_ONLY) - break; - - loc_b = blocks[i].offset; - - ret = check_file_range_equal(wr->file, wr->scratch, - SCRATCH_SIZE, loc_a, loc_b, sz); - if (ret == 0) - break; - if (ret < 0) - return ret; - } - - *out = blocks[i].offset; - if (i >= wr->file_start) - return 0; - - if (count >= (wr->file_start - i)) { - wr->blocks.used = i + count; - } else { - wr->blocks.used = wr->file_start; - } - - sz = blocks[wr->blocks.used - 1].offset + - SIZE_FROM_HASH(blocks[wr->blocks.used - 1].hash); - - return wr->file->truncate(wr->file, sz); -} - -static int align_file(block_writer_default_t *wr, sqfs_u32 flags) -{ - void *padding; - sqfs_u64 size; - size_t diff; - int ret; - - if (!(flags & SQFS_BLK_ALIGN)) - return 0; - - size = wr->file->get_size(wr->file); - diff = size % wr->devblksz; - if (diff == 0) - return 0; - - padding = calloc(1, diff); - if (padding == 0) - return SQFS_ERROR_ALLOC; - - ret = wr->file->write_at(wr->file, size, padding, diff); - free(padding); - if (ret) - return ret; - - return store_block_location(wr, size, 0, 0); -} - -static void block_writer_destroy(sqfs_object_t *wr) -{ - sqfs_drop(((block_writer_default_t *)wr)->file); - array_cleanup(&(((block_writer_default_t *)wr)->blocks)); - free(wr); -} - -static int write_data_block(sqfs_block_writer_t *base, void *user, - sqfs_u32 size, sqfs_u32 checksum, sqfs_u32 flags, - const sqfs_u8 *data, sqfs_u64 *location) -{ - block_writer_default_t *wr = (block_writer_default_t *)base; - int err; - (void)user; - - if (flags & (SQFS_BLK_FIRST_BLOCK | SQFS_BLK_FRAGMENT_BLOCK)) { - err = align_file(wr, flags); - if (err) - return err; - - if (flags & SQFS_BLK_FIRST_BLOCK) - wr->file_start = wr->blocks.used; - } - - *location = wr->file->get_size(wr->file); - - if (size != 0 && !(flags & SQFS_BLK_IS_SPARSE)) { - sqfs_u32 out = size; - if (!(flags & SQFS_BLK_IS_COMPRESSED)) - out |= 1 << 24; - - err = store_block_location(wr, *location, out, checksum); - if (err) - return err; - - err = wr->file->write_at(wr->file, *location, data, size); - if (err) - return err; - } - - if (flags & (SQFS_BLK_LAST_BLOCK | SQFS_BLK_FRAGMENT_BLOCK)) { - err = align_file(wr, flags); - if (err) - return err; - - if (flags & SQFS_BLK_LAST_BLOCK) - return deduplicate_blocks(wr, flags, location); - } - - return 0; -} - -static sqfs_u64 get_block_count(const sqfs_block_writer_t *wr) -{ - return ((const block_writer_default_t *)wr)->blocks.used; -} - -sqfs_block_writer_t *sqfs_block_writer_create(sqfs_file_t *file, - size_t devblksz, sqfs_u32 flags) -{ - block_writer_default_t *wr; - - if (flags & ~SQFS_BLOCK_WRITER_ALL_FLAGS) - return NULL; - - if (flags & SQFS_BLOCK_WRITER_HASH_COMPARE_ONLY) { - wr = calloc(1, sizeof(*wr)); - } else { - wr = alloc_flex(sizeof(*wr), 1, SCRATCH_SIZE); - } - - if (wr == NULL) - return NULL; - - sqfs_object_init(wr, block_writer_destroy, NULL); - - ((sqfs_block_writer_t *)wr)->write_data_block = write_data_block; - ((sqfs_block_writer_t *)wr)->get_block_count = get_block_count; - wr->flags = flags; - wr->file = sqfs_grab(file); - wr->devblksz = devblksz; - - if (array_init(&(wr->blocks), sizeof(blk_info_t), INIT_BLOCK_COUNT)) { - sqfs_drop(wr->file); - free(wr); - return NULL; - } - - return (sqfs_block_writer_t *)wr; -} diff --git a/lib/sqfs/comp/compressor.c b/lib/sqfs/comp/compressor.c deleted file mode 100644 index 4c4d73c..0000000 --- a/lib/sqfs/comp/compressor.c +++ /dev/null @@ -1,218 +0,0 @@ -/* SPDX-License-Identifier: LGPL-3.0-or-later */ -/* - * compressor.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#define SQFS_BUILDING_DLL -#include "config.h" - -#include -#include - -#include "internal.h" - -typedef int (*compressor_fun_t)(const sqfs_compressor_config_t *cfg, - sqfs_compressor_t **out); - -static compressor_fun_t compressors[SQFS_COMP_MAX + 1] = { -#ifdef WITH_GZIP - [SQFS_COMP_GZIP] = gzip_compressor_create, -#endif -#ifdef WITH_XZ - [SQFS_COMP_XZ] = xz_compressor_create, - [SQFS_COMP_LZMA] = lzma_compressor_create, -#endif -#ifdef WITH_LZ4 - [SQFS_COMP_LZ4] = lz4_compressor_create, -#endif -#ifdef WITH_ZSTD - [SQFS_COMP_ZSTD] = zstd_compressor_create, -#endif -}; - -static const char *names[] = { - [SQFS_COMP_GZIP] = "gzip", - [SQFS_COMP_LZMA] = "lzma", - [SQFS_COMP_LZO] = "lzo", - [SQFS_COMP_XZ] = "xz", - [SQFS_COMP_LZ4] = "lz4", - [SQFS_COMP_ZSTD] = "zstd", -}; - -int sqfs_generic_write_options(sqfs_file_t *file, const void *data, size_t size) -{ - sqfs_u8 buffer[64]; - sqfs_u16 header; - int ret; - - /* XXX: options for all known compressors should fit into this */ - if (size >= (sizeof(buffer) - sizeof(header))) - return SQFS_ERROR_INTERNAL; - - header = htole16(0x8000 | size); - memcpy(buffer, &header, sizeof(header)); - memcpy(buffer + sizeof(header), data, size); - - ret = file->write_at(file, sizeof(sqfs_super_t), - buffer, sizeof(header) + size); - if (ret) - return ret; - - return sizeof(header) + size; -} - -int sqfs_generic_read_options(sqfs_file_t *file, void *data, size_t size) -{ - sqfs_u8 buffer[64]; - sqfs_u16 header; - int ret; - - /* XXX: options for all known compressors should fit into this */ - if (size >= (sizeof(buffer) - sizeof(header))) - return SQFS_ERROR_INTERNAL; - - ret = file->read_at(file, sizeof(sqfs_super_t), - buffer, sizeof(header) + size); - if (ret) - return ret; - - memcpy(&header, buffer, sizeof(header)); - - if (le16toh(header) != (0x8000 | size)) - return SQFS_ERROR_CORRUPTED; - - memcpy(data, buffer + 2, size); - return 0; -} - -int sqfs_compressor_create(const sqfs_compressor_config_t *cfg, - sqfs_compressor_t **out) -{ - sqfs_u8 padd0[sizeof(cfg->opt)]; - int ret; - - *out = NULL; - - /* check compressor ID */ - if (cfg == NULL) - return SQFS_ERROR_ARG_INVALID; - - if (cfg->id < SQFS_COMP_MIN || cfg->id > SQFS_COMP_MAX) - return SQFS_ERROR_UNSUPPORTED; - - if (compressors[cfg->id] == NULL) - return SQFS_ERROR_UNSUPPORTED; - - /* make sure the padding bytes are cleared, so we could theoretically - turn them into option fields in the future and remain compatible */ - memset(padd0, 0, sizeof(padd0)); - - switch (cfg->id) { - case SQFS_COMP_XZ: - ret = memcmp(cfg->opt.xz.padd0, padd0, - sizeof(cfg->opt.xz.padd0)); - break; - case SQFS_COMP_LZMA: - ret = memcmp(cfg->opt.lzma.padd0, padd0, - sizeof(cfg->opt.lzma.padd0)); - break; - case SQFS_COMP_LZO: - ret = memcmp(cfg->opt.lzo.padd0, padd0, - sizeof(cfg->opt.lzo.padd0)); - break; - case SQFS_COMP_GZIP: - ret = memcmp(cfg->opt.gzip.padd0, padd0, - sizeof(cfg->opt.gzip.padd0)); - break; - default: - ret = memcmp(cfg->opt.padd0, padd0, sizeof(cfg->opt.padd0)); - break; - } - - if (ret != 0) - return SQFS_ERROR_ARG_INVALID; - - return compressors[cfg->id](cfg, out); -} - -const char *sqfs_compressor_name_from_id(SQFS_COMPRESSOR id) -{ - if (id < 0 || (size_t)id >= sizeof(names) / sizeof(names[0])) - return NULL; - - return names[id]; -} - -int sqfs_compressor_id_from_name(const char *name) -{ - size_t i; - - for (i = 0; i < sizeof(names) / sizeof(names[0]); ++i) { - if (names[i] != NULL && strcmp(names[i], name) == 0) - return i; - } - - return SQFS_ERROR_UNSUPPORTED; -} - -int sqfs_compressor_config_init(sqfs_compressor_config_t *cfg, - SQFS_COMPRESSOR id, - size_t block_size, sqfs_u16 flags) -{ - sqfs_u32 flag_mask = SQFS_COMP_FLAG_GENERIC_ALL; - - memset(cfg, 0, sizeof(*cfg)); - - switch (id) { - case SQFS_COMP_GZIP: - flag_mask |= SQFS_COMP_FLAG_GZIP_ALL; - cfg->level = SQFS_GZIP_DEFAULT_LEVEL; - cfg->opt.gzip.window_size = SQFS_GZIP_DEFAULT_WINDOW; - break; - case SQFS_COMP_LZO: - cfg->opt.lzo.algorithm = SQFS_LZO_DEFAULT_ALG; - cfg->level = SQFS_LZO_DEFAULT_LEVEL; - break; - case SQFS_COMP_ZSTD: - cfg->level = SQFS_ZSTD_DEFAULT_LEVEL; - break; - case SQFS_COMP_XZ: - flag_mask |= SQFS_COMP_FLAG_XZ_ALL; - cfg->level = SQFS_XZ_DEFAULT_LEVEL; - cfg->opt.xz.dict_size = block_size; - cfg->opt.xz.lc = SQFS_XZ_DEFAULT_LC; - cfg->opt.xz.lp = SQFS_XZ_DEFAULT_LP; - cfg->opt.xz.pb = SQFS_XZ_DEFAULT_PB; - - if (block_size < SQFS_XZ_MIN_DICT_SIZE) - cfg->opt.xz.dict_size = SQFS_XZ_MIN_DICT_SIZE; - break; - case SQFS_COMP_LZMA: - flag_mask |= SQFS_COMP_FLAG_LZMA_ALL; - cfg->level = SQFS_LZMA_DEFAULT_LEVEL; - cfg->opt.lzma.dict_size = block_size; - cfg->opt.lzma.lc = SQFS_LZMA_DEFAULT_LC; - cfg->opt.lzma.lp = SQFS_LZMA_DEFAULT_LP; - cfg->opt.lzma.pb = SQFS_LZMA_DEFAULT_PB; - - if (block_size < SQFS_LZMA_MIN_DICT_SIZE) - cfg->opt.lzma.dict_size = SQFS_LZMA_MIN_DICT_SIZE; - break; - case SQFS_COMP_LZ4: - flag_mask |= SQFS_COMP_FLAG_LZ4_ALL; - break; - default: - return SQFS_ERROR_UNSUPPORTED; - } - - if (flags & ~flag_mask) { - memset(cfg, 0, sizeof(*cfg)); - return SQFS_ERROR_UNSUPPORTED; - } - - cfg->id = id; - cfg->flags = flags; - cfg->block_size = block_size; - return 0; -} diff --git a/lib/sqfs/comp/gzip.c b/lib/sqfs/comp/gzip.c deleted file mode 100644 index beacfb8..0000000 --- a/lib/sqfs/comp/gzip.c +++ /dev/null @@ -1,307 +0,0 @@ -/* SPDX-License-Identifier: LGPL-3.0-or-later */ -/* - * gzip.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#define SQFS_BUILDING_DLL -#include "config.h" - -#include -#include -#include -#include -#include - -#include "internal.h" - -typedef struct { - sqfs_u32 level; - sqfs_u16 window; - sqfs_u16 strategies; -} gzip_options_t; - -typedef struct { - sqfs_compressor_t base; - - z_stream strm; - bool compress; - - size_t block_size; - gzip_options_t opt; -} gzip_compressor_t; - -static void gzip_destroy(sqfs_object_t *base) -{ - gzip_compressor_t *gzip = (gzip_compressor_t *)base; - - if (gzip->compress) { - deflateEnd(&gzip->strm); - } else { - inflateEnd(&gzip->strm); - } - - free(gzip); -} - -static void gzip_get_configuration(const sqfs_compressor_t *base, - sqfs_compressor_config_t *cfg) -{ - const gzip_compressor_t *gzip = (const gzip_compressor_t *)base; - - memset(cfg, 0, sizeof(*cfg)); - cfg->id = SQFS_COMP_GZIP; - cfg->flags = gzip->opt.strategies; - cfg->block_size = gzip->block_size; - cfg->level = gzip->opt.level; - cfg->opt.gzip.window_size = gzip->opt.window; - - if (!gzip->compress) - cfg->flags |= SQFS_COMP_FLAG_UNCOMPRESS; -} - -static int gzip_write_options(sqfs_compressor_t *base, sqfs_file_t *file) -{ - gzip_compressor_t *gzip = (gzip_compressor_t *)base; - gzip_options_t opt; - - if (gzip->opt.level == SQFS_GZIP_DEFAULT_LEVEL && - gzip->opt.window == SQFS_GZIP_DEFAULT_WINDOW && - gzip->opt.strategies == 0) { - return 0; - } - - opt.level = htole32(gzip->opt.level); - opt.window = htole16(gzip->opt.window); - opt.strategies = htole16(gzip->opt.strategies); - - return sqfs_generic_write_options(file, &opt, sizeof(opt)); -} - -static int gzip_read_options(sqfs_compressor_t *base, sqfs_file_t *file) -{ - gzip_compressor_t *gzip = (gzip_compressor_t *)base; - gzip_options_t opt; - int ret; - - ret = sqfs_generic_read_options(file, &opt, sizeof(opt)); - if (ret) - return ret; - - gzip->opt.level = le32toh(opt.level); - gzip->opt.window = le16toh(opt.window); - gzip->opt.strategies = le16toh(opt.strategies); - - if (gzip->opt.level < 1 || gzip->opt.level > 9) - return SQFS_ERROR_UNSUPPORTED; - - if (gzip->opt.window < 8 || gzip->opt.window > 15) - return SQFS_ERROR_UNSUPPORTED; - - if (gzip->opt.strategies & ~SQFS_COMP_FLAG_GZIP_ALL) - return SQFS_ERROR_UNSUPPORTED; - - return 0; -} - -static int flag_to_zlib_strategy(int flag) -{ - switch (flag) { - case SQFS_COMP_FLAG_GZIP_DEFAULT: - return Z_DEFAULT_STRATEGY; - case SQFS_COMP_FLAG_GZIP_FILTERED: - return Z_FILTERED; - case SQFS_COMP_FLAG_GZIP_HUFFMAN: - return Z_HUFFMAN_ONLY; - case SQFS_COMP_FLAG_GZIP_RLE: - return Z_RLE; - case SQFS_COMP_FLAG_GZIP_FIXED: - return Z_FIXED; - default: - break; - } - - return 0; -} - -static int find_strategy(gzip_compressor_t *gzip, const sqfs_u8 *in, - sqfs_u32 size, sqfs_u8 *out, sqfs_u32 outsize) -{ - int ret, strategy, selected = Z_DEFAULT_STRATEGY; - size_t i, length, minlength = 0; - - for (i = 0x01; i & SQFS_COMP_FLAG_GZIP_ALL; i <<= 1) { - if ((gzip->opt.strategies & i) == 0) - continue; - - ret = deflateReset(&gzip->strm); - if (ret != Z_OK) - return SQFS_ERROR_COMPRESSOR; - - strategy = flag_to_zlib_strategy(i); - - gzip->strm.next_in = (z_const Bytef *)in; - gzip->strm.avail_in = size; - gzip->strm.next_out = out; - gzip->strm.avail_out = outsize; - - ret = deflateParams(&gzip->strm, gzip->opt.level, strategy); - if (ret != Z_OK) - return SQFS_ERROR_COMPRESSOR; - - ret = deflate(&gzip->strm, Z_FINISH); - - if (ret == Z_STREAM_END) { - length = gzip->strm.total_out; - - if (minlength == 0 || length < minlength) { - minlength = length; - selected = strategy; - } - } else if (ret != Z_OK && ret != Z_BUF_ERROR) { - return SQFS_ERROR_COMPRESSOR; - } - } - - return selected; -} - -static sqfs_s32 gzip_do_block(sqfs_compressor_t *base, const sqfs_u8 *in, - sqfs_u32 size, sqfs_u8 *out, sqfs_u32 outsize) -{ - gzip_compressor_t *gzip = (gzip_compressor_t *)base; - int ret, strategy = 0; - size_t written; - - if (size >= 0x7FFFFFFF) - return SQFS_ERROR_ARG_INVALID; - - if (gzip->compress && gzip->opt.strategies != 0) { - strategy = find_strategy(gzip, in, size, out, outsize); - if (strategy < 0) - return strategy; - } - - if (gzip->compress) { - ret = deflateReset(&gzip->strm); - } else { - ret = inflateReset(&gzip->strm); - } - - if (ret != Z_OK) - return SQFS_ERROR_COMPRESSOR; - - gzip->strm.next_in = (const void *)in; - gzip->strm.avail_in = size; - gzip->strm.next_out = out; - gzip->strm.avail_out = outsize; - - if (gzip->compress && gzip->opt.strategies != 0) { - ret = deflateParams(&gzip->strm, gzip->opt.level, strategy); - if (ret != Z_OK) - return SQFS_ERROR_COMPRESSOR; - } - - if (gzip->compress) { - ret = deflate(&gzip->strm, Z_FINISH); - } else { - ret = inflate(&gzip->strm, Z_FINISH); - } - - if (ret == Z_STREAM_END) { - written = gzip->strm.total_out; - - if (gzip->compress && written >= size) - return 0; - - return written; - } - - if (ret != Z_OK && ret != Z_BUF_ERROR) - return SQFS_ERROR_COMPRESSOR; - - return 0; -} - -static sqfs_object_t *gzip_create_copy(const sqfs_object_t *cmp) -{ - gzip_compressor_t *gzip = malloc(sizeof(*gzip)); - int ret; - - if (gzip == NULL) - return NULL; - - memcpy(gzip, cmp, sizeof(*gzip)); - memset(&gzip->strm, 0, sizeof(gzip->strm)); - - if (gzip->compress) { - ret = deflateInit2(&gzip->strm, gzip->opt.level, Z_DEFLATED, - gzip->opt.window, 8, Z_DEFAULT_STRATEGY); - } else { - ret = inflateInit(&gzip->strm); - } - - if (ret != Z_OK) { - free(gzip); - return NULL; - } - - return (sqfs_object_t *)gzip; -} - -int gzip_compressor_create(const sqfs_compressor_config_t *cfg, - sqfs_compressor_t **out) -{ - gzip_compressor_t *gzip; - sqfs_compressor_t *base; - int ret; - - if (cfg->flags & ~(SQFS_COMP_FLAG_GZIP_ALL | - SQFS_COMP_FLAG_GENERIC_ALL)) { - return SQFS_ERROR_UNSUPPORTED; - } - - if (cfg->level < SQFS_GZIP_MIN_LEVEL || - cfg->level > SQFS_GZIP_MAX_LEVEL) { - return SQFS_ERROR_UNSUPPORTED; - } - - if (cfg->opt.gzip.window_size < SQFS_GZIP_MIN_WINDOW || - cfg->opt.gzip.window_size > SQFS_GZIP_MAX_WINDOW) { - return SQFS_ERROR_UNSUPPORTED; - } - - gzip = calloc(1, sizeof(*gzip)); - base = (sqfs_compressor_t *)gzip; - - if (gzip == NULL) - return SQFS_ERROR_ALLOC; - - sqfs_object_init(gzip, gzip_destroy, gzip_create_copy); - - gzip->opt.level = cfg->level; - gzip->opt.window = cfg->opt.gzip.window_size; - gzip->opt.strategies = cfg->flags & SQFS_COMP_FLAG_GZIP_ALL; - gzip->compress = (cfg->flags & SQFS_COMP_FLAG_UNCOMPRESS) == 0; - gzip->block_size = cfg->block_size; - base->get_configuration = gzip_get_configuration; - base->do_block = gzip_do_block; - base->write_options = gzip_write_options; - base->read_options = gzip_read_options; - - if (gzip->compress) { - ret = deflateInit2(&gzip->strm, cfg->level, - Z_DEFLATED, cfg->opt.gzip.window_size, 8, - Z_DEFAULT_STRATEGY); - } else { - ret = inflateInit(&gzip->strm); - } - - if (ret != Z_OK) { - free(gzip); - return SQFS_ERROR_COMPRESSOR; - } - - *out = base; - return 0; -} diff --git a/lib/sqfs/comp/internal.h b/lib/sqfs/comp/internal.h deleted file mode 100644 index e4c3dd8..0000000 --- a/lib/sqfs/comp/internal.h +++ /dev/null @@ -1,46 +0,0 @@ -/* SPDX-License-Identifier: LGPL-3.0-or-later */ -/* - * internal.h - * - * Copyright (C) 2019 David Oberhollenzer - */ -#ifndef INTERNAL_H -#define INTERNAL_H - -#include "config.h" - -#include "sqfs/predef.h" -#include "sqfs/compressor.h" -#include "sqfs/error.h" -#include "sqfs/block.h" -#include "sqfs/io.h" -#include "util/util.h" - -SQFS_INTERNAL -int sqfs_generic_write_options(sqfs_file_t *file, const void *data, - size_t size); - -SQFS_INTERNAL -int sqfs_generic_read_options(sqfs_file_t *file, void *data, size_t size); - -SQFS_INTERNAL -int xz_compressor_create(const sqfs_compressor_config_t *cfg, - sqfs_compressor_t **out); - -SQFS_INTERNAL -int gzip_compressor_create(const sqfs_compressor_config_t *cfg, - sqfs_compressor_t **out); - -SQFS_INTERNAL -int lz4_compressor_create(const sqfs_compressor_config_t *cfg, - sqfs_compressor_t **out); - -SQFS_INTERNAL -int zstd_compressor_create(const sqfs_compressor_config_t *cfg, - sqfs_compressor_t **out); - -SQFS_INTERNAL -int lzma_compressor_create(const sqfs_compressor_config_t *cfg, - sqfs_compressor_t **out); - -#endif /* INTERNAL_H */ diff --git a/lib/sqfs/comp/lz4.c b/lib/sqfs/comp/lz4.c deleted file mode 100644 index 77f4a6e..0000000 --- a/lib/sqfs/comp/lz4.c +++ /dev/null @@ -1,172 +0,0 @@ -/* SPDX-License-Identifier: LGPL-3.0-or-later */ -/* - * lz4.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#define SQFS_BUILDING_DLL -#include "config.h" - -#include -#include -#include - -#include -#include - -#include "internal.h" - -typedef struct { - sqfs_compressor_t base; - size_t block_size; - bool high_compression; -} lz4_compressor_t; - -typedef struct { - sqfs_u32 version; - sqfs_u32 flags; -} lz4_options; - -#define LZ4LEGACY 1 - -/* old verions of liblz4 don't have this */ -#ifndef LZ4HC_CLEVEL_MAX -#define LZ4HC_CLEVEL_MAX 12 -#endif - -static int lz4_write_options(sqfs_compressor_t *base, sqfs_file_t *file) -{ - lz4_compressor_t *lz4 = (lz4_compressor_t *)base; - lz4_options opt = { - .version = htole32(LZ4LEGACY), - .flags = htole32(lz4->high_compression ? - SQFS_COMP_FLAG_LZ4_HC : 0), - }; - - return sqfs_generic_write_options(file, &opt, sizeof(opt)); -} - -static int lz4_read_options(sqfs_compressor_t *base, sqfs_file_t *file) -{ - lz4_options opt; - int ret; - (void)base; - - ret = sqfs_generic_read_options(file, &opt, sizeof(opt)); - if (ret) - return ret; - - opt.version = le32toh(opt.version); - opt.flags = le32toh(opt.flags); - - if (opt.version != LZ4LEGACY) - return SQFS_ERROR_UNSUPPORTED; - - return 0; -} - -static sqfs_s32 lz4_comp_block(sqfs_compressor_t *base, const sqfs_u8 *in, - sqfs_u32 size, sqfs_u8 *out, sqfs_u32 outsize) -{ - lz4_compressor_t *lz4 = (lz4_compressor_t *)base; - int ret; - - if (size >= 0x7FFFFFFF) - return SQFS_ERROR_ARG_INVALID; - - if (lz4->high_compression) { - ret = LZ4_compress_HC((const void *)in, (void *)out, - size, outsize, LZ4HC_CLEVEL_MAX); - } else { - ret = LZ4_compress_default((const void *)in, (void *)out, - size, outsize); - } - - if (ret < 0) - return SQFS_ERROR_COMPRESSOR; - - return ret; -} - -static sqfs_s32 lz4_uncomp_block(sqfs_compressor_t *base, const sqfs_u8 *in, - sqfs_u32 size, sqfs_u8 *out, sqfs_u32 outsize) -{ - int ret; - (void)base; - - if (outsize >= 0x7FFFFFFF) - return SQFS_ERROR_ARG_INVALID; - - ret = LZ4_decompress_safe((const void *)in, (void *)out, size, outsize); - - if (ret < 0) - return SQFS_ERROR_COMPRESSOR; - - return ret; -} - -static void lz4_get_configuration(const sqfs_compressor_t *base, - sqfs_compressor_config_t *cfg) -{ - const lz4_compressor_t *lz4 = (const lz4_compressor_t *)base; - - memset(cfg, 0, sizeof(*cfg)); - cfg->id = SQFS_COMP_LZ4; - cfg->block_size = lz4->block_size; - - if (lz4->high_compression) - cfg->flags |= SQFS_COMP_FLAG_LZ4_HC; - - if (base->do_block == lz4_uncomp_block) - cfg->flags |= SQFS_COMP_FLAG_UNCOMPRESS; -} - -static sqfs_object_t *lz4_create_copy(const sqfs_object_t *cmp) -{ - lz4_compressor_t *lz4 = malloc(sizeof(*lz4)); - - if (lz4 == NULL) - return NULL; - - memcpy(lz4, cmp, sizeof(*lz4)); - return (sqfs_object_t *)lz4; -} - -static void lz4_destroy(sqfs_object_t *base) -{ - free(base); -} - -int lz4_compressor_create(const sqfs_compressor_config_t *cfg, - sqfs_compressor_t **out) -{ - sqfs_compressor_t *base; - lz4_compressor_t *lz4; - - if (cfg->flags & ~(SQFS_COMP_FLAG_LZ4_ALL | - SQFS_COMP_FLAG_GENERIC_ALL)) { - return SQFS_ERROR_UNSUPPORTED; - } - - if (cfg->level != 0) - return SQFS_ERROR_UNSUPPORTED; - - lz4 = calloc(1, sizeof(*lz4)); - base = (sqfs_compressor_t *)lz4; - if (lz4 == NULL) - return SQFS_ERROR_ALLOC; - - sqfs_object_init(lz4, lz4_destroy, lz4_create_copy); - - lz4->high_compression = (cfg->flags & SQFS_COMP_FLAG_LZ4_HC) != 0; - lz4->block_size = cfg->block_size; - - base->get_configuration = lz4_get_configuration; - base->do_block = (cfg->flags & SQFS_COMP_FLAG_UNCOMPRESS) ? - lz4_uncomp_block : lz4_comp_block; - base->write_options = lz4_write_options; - base->read_options = lz4_read_options; - - *out = base; - return 0; -} diff --git a/lib/sqfs/comp/lzma.c b/lib/sqfs/comp/lzma.c deleted file mode 100644 index 5456603..0000000 --- a/lib/sqfs/comp/lzma.c +++ /dev/null @@ -1,281 +0,0 @@ -/* SPDX-License-Identifier: LGPL-3.0-or-later */ -/* - * lzma.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#define SQFS_BUILDING_DLL -#include "config.h" - -#include -#include -#include -#include - -#include "internal.h" - -#define LZMA_SIZE_OFFSET (5) -#define LZMA_SIZE_BYTES (8) -#define LZMA_HEADER_SIZE (13) - -#define MEMLIMIT (64 * 1024 * 1024) - -typedef struct { - sqfs_compressor_t base; - size_t block_size; - size_t dict_size; - - sqfs_u32 flags; - sqfs_u8 level; - sqfs_u8 lc; - sqfs_u8 lp; - sqfs_u8 pb; -} lzma_compressor_t; - -static int lzma_write_options(sqfs_compressor_t *base, sqfs_file_t *file) -{ - (void)base; (void)file; - return 0; -} - -static int lzma_read_options(sqfs_compressor_t *base, sqfs_file_t *file) -{ - (void)base; - (void)file; - return SQFS_ERROR_UNSUPPORTED; -} - -static sqfs_s32 try_compress(lzma_compressor_t *lzma, sqfs_u32 preset, - const sqfs_u8 *in, size_t size, - sqfs_u8 *out, size_t outsize) -{ - lzma_stream strm = LZMA_STREAM_INIT; - lzma_options_lzma opt; - int ret; - - lzma_lzma_preset(&opt, preset); - opt.dict_size = lzma->block_size; - opt.lc = lzma->lc; - opt.lp = lzma->lp; - opt.pb = lzma->pb; - - if (lzma_alone_encoder(&strm, &opt) != LZMA_OK) { - lzma_end(&strm); - return SQFS_ERROR_COMPRESSOR; - } - - strm.next_out = out; - strm.avail_out = outsize; - strm.next_in = in; - strm.avail_in = size; - - ret = lzma_code(&strm, LZMA_FINISH); - lzma_end(&strm); - - if (ret != LZMA_STREAM_END) - return ret == LZMA_OK ? 0 : SQFS_ERROR_COMPRESSOR; - - if (strm.total_out > size) - return 0; - - out[LZMA_SIZE_OFFSET ] = size & 0xFF; - out[LZMA_SIZE_OFFSET + 1] = (size >> 8) & 0xFF; - out[LZMA_SIZE_OFFSET + 2] = (size >> 16) & 0xFF; - out[LZMA_SIZE_OFFSET + 3] = (size >> 24) & 0xFF; - out[LZMA_SIZE_OFFSET + 4] = 0; - out[LZMA_SIZE_OFFSET + 5] = 0; - out[LZMA_SIZE_OFFSET + 6] = 0; - out[LZMA_SIZE_OFFSET + 7] = 0; - return strm.total_out; -} - -static sqfs_s32 lzma_comp_block(sqfs_compressor_t *base, const sqfs_u8 *in, - sqfs_u32 size, sqfs_u8 *out, sqfs_u32 outsize) -{ - lzma_compressor_t *lzma = (lzma_compressor_t *)base; - sqfs_s32 ret, smallest; - sqfs_u32 preset; - - if (outsize < LZMA_HEADER_SIZE || size >= 0x7FFFFFFF) - return SQFS_ERROR_ARG_INVALID; - - preset = lzma->level; - ret = try_compress(lzma, preset, in, size, out, outsize); - if (ret < 0 || !(lzma->flags & SQFS_COMP_FLAG_LZMA_EXTREME)) - return ret; - - preset |= LZMA_PRESET_EXTREME; - smallest = ret; - - ret = try_compress(lzma, preset, in, size, out, outsize); - if (ret < 0 || (ret > 0 && (smallest == 0 || ret < smallest))) - return ret; - - preset &= ~LZMA_PRESET_EXTREME; - return smallest == 0 ? 0 : - try_compress(lzma, preset, in, size, out, outsize); -} - -static sqfs_s32 lzma_uncomp_block(sqfs_compressor_t *base, const sqfs_u8 *in, - sqfs_u32 size, sqfs_u8 *out, sqfs_u32 outsize) -{ - sqfs_u8 lzma_header[LZMA_HEADER_SIZE]; - lzma_stream strm = LZMA_STREAM_INIT; - size_t hdrsize; - int ret; - (void)base; - - if (size >= 0x7FFFFFFF) - return SQFS_ERROR_ARG_INVALID; - - if (size < sizeof(lzma_header)) - return SQFS_ERROR_CORRUPTED; - - hdrsize = (size_t)in[LZMA_SIZE_OFFSET] | - ((size_t)in[LZMA_SIZE_OFFSET + 1] << 8) | - ((size_t)in[LZMA_SIZE_OFFSET + 2] << 16) | - ((size_t)in[LZMA_SIZE_OFFSET + 3] << 24); - - if (hdrsize > outsize) - return 0; - - if (lzma_alone_decoder(&strm, MEMLIMIT) != LZMA_OK) { - lzma_end(&strm); - return SQFS_ERROR_COMPRESSOR; - } - - memcpy(lzma_header, in, sizeof(lzma_header)); - memset(lzma_header + LZMA_SIZE_OFFSET, 0xFF, LZMA_SIZE_BYTES); - - strm.next_out = out; - strm.avail_out = outsize; - strm.next_in = lzma_header; - strm.avail_in = sizeof(lzma_header); - - ret = lzma_code(&strm, LZMA_RUN); - - if (ret != LZMA_OK || strm.avail_in != 0) { - lzma_end(&strm); - return SQFS_ERROR_COMPRESSOR; - } - - strm.next_in = in + sizeof(lzma_header); - strm.avail_in = size - sizeof(lzma_header); - - ret = lzma_code(&strm, LZMA_FINISH); - lzma_end(&strm); - - if (ret != LZMA_STREAM_END && ret != LZMA_OK) - return SQFS_ERROR_COMPRESSOR; - - if (ret == LZMA_OK) { - if (strm.total_out < hdrsize || strm.avail_in != 0) - return 0; - } - - return hdrsize; -} - -static void lzma_get_configuration(const sqfs_compressor_t *base, - sqfs_compressor_config_t *cfg) -{ - const lzma_compressor_t *lzma = (const lzma_compressor_t *)base; - - memset(cfg, 0, sizeof(*cfg)); - cfg->id = SQFS_COMP_LZMA; - cfg->block_size = lzma->block_size; - cfg->flags = lzma->flags; - cfg->level = lzma->level; - cfg->opt.lzma.dict_size = lzma->dict_size; - cfg->opt.lzma.lc = lzma->lc; - cfg->opt.lzma.lp = lzma->lp; - cfg->opt.lzma.pb = lzma->pb; -} - -static sqfs_object_t *lzma_create_copy(const sqfs_object_t *cmp) -{ - lzma_compressor_t *copy = malloc(sizeof(*copy)); - - if (copy != NULL) - memcpy(copy, cmp, sizeof(*copy)); - - return (sqfs_object_t *)copy; -} - -static void lzma_destroy(sqfs_object_t *base) -{ - free(base); -} - -int lzma_compressor_create(const sqfs_compressor_config_t *cfg, - sqfs_compressor_t **out) -{ - sqfs_compressor_t *base; - lzma_compressor_t *lzma; - sqfs_u32 mask; - - mask = SQFS_COMP_FLAG_GENERIC_ALL | SQFS_COMP_FLAG_LZMA_ALL; - - if (cfg->flags & ~mask) - return SQFS_ERROR_UNSUPPORTED; - - /* XXX: values are unsigned and minimum is 0 */ - if (cfg->level > SQFS_LZMA_MAX_LEVEL) - return SQFS_ERROR_UNSUPPORTED; - - if (cfg->opt.lzma.lc > SQFS_LZMA_MAX_LC) - return SQFS_ERROR_UNSUPPORTED; - - if (cfg->opt.lzma.lp > SQFS_LZMA_MAX_LP) - return SQFS_ERROR_UNSUPPORTED; - - if (cfg->opt.lzma.pb > SQFS_LZMA_MAX_PB) - return SQFS_ERROR_UNSUPPORTED; - - if (cfg->opt.lzma.lc + cfg->opt.lzma.lp > 4) - return SQFS_ERROR_UNSUPPORTED; - - if (cfg->opt.lzma.dict_size == 0) - return SQFS_ERROR_UNSUPPORTED; - - if (cfg->opt.lzma.dict_size < SQFS_LZMA_MIN_DICT_SIZE) - return SQFS_ERROR_UNSUPPORTED; - - if (cfg->opt.lzma.dict_size > SQFS_LZMA_MAX_DICT_SIZE) - return SQFS_ERROR_UNSUPPORTED; - - mask = cfg->opt.lzma.dict_size; - mask &= mask - 1; - - if (mask != 0) { - if ((mask & (mask - 1)) != 0) - return SQFS_ERROR_UNSUPPORTED; - - if (cfg->opt.lzma.dict_size != (mask | mask >> 1)) - return SQFS_ERROR_UNSUPPORTED; - } - - lzma = calloc(1, sizeof(*lzma)); - base = (sqfs_compressor_t *)lzma; - if (lzma == NULL) - return SQFS_ERROR_ALLOC; - - sqfs_object_init(lzma, lzma_destroy, lzma_create_copy); - - lzma->block_size = cfg->block_size; - lzma->flags = cfg->flags; - lzma->level = cfg->level; - lzma->dict_size = cfg->opt.lzma.dict_size; - lzma->lc = cfg->opt.lzma.lc; - lzma->lp = cfg->opt.lzma.lp; - lzma->pb = cfg->opt.lzma.pb; - - base->get_configuration = lzma_get_configuration; - base->do_block = (cfg->flags & SQFS_COMP_FLAG_UNCOMPRESS) ? - lzma_uncomp_block : lzma_comp_block; - base->write_options = lzma_write_options; - base->read_options = lzma_read_options; - - *out = base; - return 0; -} diff --git a/lib/sqfs/comp/xz.c b/lib/sqfs/comp/xz.c deleted file mode 100644 index 13545ed..0000000 --- a/lib/sqfs/comp/xz.c +++ /dev/null @@ -1,324 +0,0 @@ -/* SPDX-License-Identifier: LGPL-3.0-or-later */ -/* - * xz.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#define SQFS_BUILDING_DLL -#include "config.h" - -#include -#include -#include -#include - -#include "internal.h" - -typedef struct { - sqfs_compressor_t base; - size_t block_size; - size_t dict_size; - - sqfs_u8 level; - sqfs_u8 lc; - sqfs_u8 lp; - sqfs_u8 pb; - - int flags; -} xz_compressor_t; - -typedef struct { - sqfs_u32 dict_size; - sqfs_u32 flags; -} xz_options_t; - -static bool is_dict_size_valid(size_t size) -{ - size_t x = size & (size - 1); - - if (x == 0) - return true; - - return size == (x | (x >> 1)); -} - -static int xz_write_options(sqfs_compressor_t *base, sqfs_file_t *file) -{ - xz_compressor_t *xz = (xz_compressor_t *)base; - xz_options_t opt; - sqfs_u32 flags; - - if (xz->flags == 0 && xz->dict_size == xz->block_size) - return 0; - - flags = xz->flags & SQFS_COMP_FLAG_XZ_ALL; - flags &= ~SQFS_COMP_FLAG_XZ_EXTREME; - - opt.dict_size = htole32(xz->dict_size); - opt.flags = htole32(flags); - - return sqfs_generic_write_options(file, &opt, sizeof(opt)); -} - -static int xz_read_options(sqfs_compressor_t *base, sqfs_file_t *file) -{ - xz_compressor_t *xz = (xz_compressor_t *)base; - xz_options_t opt; - int ret; - - ret = sqfs_generic_read_options(file, &opt, sizeof(opt)); - if (ret) - return ret; - - opt.dict_size = le32toh(opt.dict_size); - opt.flags = le32toh(opt.flags); - - if (!is_dict_size_valid(opt.dict_size)) - return SQFS_ERROR_CORRUPTED; - - if (opt.flags & ~SQFS_COMP_FLAG_XZ_ALL) - return SQFS_ERROR_UNSUPPORTED; - - xz->flags = opt.flags; - xz->dict_size = opt.dict_size; - return 0; -} - -static sqfs_s32 compress(xz_compressor_t *xz, lzma_vli filter, - const sqfs_u8 *in, sqfs_u32 size, - sqfs_u8 *out, sqfs_u32 outsize, - sqfs_u32 presets) -{ - lzma_filter filters[5]; - lzma_options_lzma opt; - size_t written = 0; - lzma_ret ret; - int i = 0; - - if (lzma_lzma_preset(&opt, presets)) - return SQFS_ERROR_COMPRESSOR; - - opt.lc = xz->lc; - opt.lp = xz->lp; - opt.pb = xz->pb; - opt.dict_size = xz->dict_size; - - if (filter != LZMA_VLI_UNKNOWN) { - filters[i].id = filter; - filters[i].options = NULL; - ++i; - } - - filters[i].id = LZMA_FILTER_LZMA2; - filters[i].options = &opt; - ++i; - - filters[i].id = LZMA_VLI_UNKNOWN; - filters[i].options = NULL; - ++i; - - ret = lzma_stream_buffer_encode(filters, LZMA_CHECK_CRC32, NULL, - in, size, out, &written, outsize); - - if (ret == LZMA_OK) - return (written >= size) ? 0 : written; - - if (ret != LZMA_BUF_ERROR) - return SQFS_ERROR_COMPRESSOR; - - return 0; -} - -static lzma_vli flag_to_vli(int flag) -{ - switch (flag) { - case SQFS_COMP_FLAG_XZ_X86: - return LZMA_FILTER_X86; - case SQFS_COMP_FLAG_XZ_POWERPC: - return LZMA_FILTER_POWERPC; - case SQFS_COMP_FLAG_XZ_IA64: - return LZMA_FILTER_IA64; - case SQFS_COMP_FLAG_XZ_ARM: - return LZMA_FILTER_ARM; - case SQFS_COMP_FLAG_XZ_ARMTHUMB: - return LZMA_FILTER_ARMTHUMB; - case SQFS_COMP_FLAG_XZ_SPARC: - return LZMA_FILTER_SPARC; - default: - break; - } - - return LZMA_VLI_UNKNOWN; -} - -static sqfs_s32 xz_comp_block(sqfs_compressor_t *base, const sqfs_u8 *in, - sqfs_u32 size, sqfs_u8 *out, sqfs_u32 outsize) -{ - xz_compressor_t *xz = (xz_compressor_t *)base; - lzma_vli filter, selected = LZMA_VLI_UNKNOWN; - sqfs_s32 ret, smallest; - bool extreme; - size_t i; - - if (size >= 0x7FFFFFFF) - return SQFS_ERROR_ARG_INVALID; - - ret = compress(xz, LZMA_VLI_UNKNOWN, in, size, out, - outsize, xz->level); - if (ret < 0 || xz->flags == 0) - return ret; - - smallest = ret; - extreme = false; - - if (xz->flags & SQFS_COMP_FLAG_XZ_EXTREME) { - ret = compress(xz, LZMA_VLI_UNKNOWN, in, size, out, outsize, - xz->level | LZMA_PRESET_EXTREME); - - if (ret > 0 && (smallest == 0 || ret < smallest)) { - smallest = ret; - extreme = true; - } - } - - for (i = 1; i & SQFS_COMP_FLAG_XZ_ALL; i <<= 1) { - if ((i & SQFS_COMP_FLAG_XZ_EXTREME) || (xz->flags & i) == 0) - continue; - - filter = flag_to_vli(i); - - ret = compress(xz, filter, in, size, out, outsize, xz->level); - if (ret > 0 && (smallest == 0 || ret < smallest)) { - smallest = ret; - selected = filter; - extreme = false; - } - - if (xz->flags & SQFS_COMP_FLAG_XZ_EXTREME) { - ret = compress(xz, filter, in, size, out, outsize, - xz->level | LZMA_PRESET_EXTREME); - - if (ret > 0 && (smallest == 0 || ret < smallest)) { - smallest = ret; - selected = filter; - extreme = true; - } - } - } - - if (smallest == 0) - return 0; - - return compress(xz, selected, in, size, out, outsize, - xz->level | (extreme ? LZMA_PRESET_EXTREME : 0)); -} - -static sqfs_s32 xz_uncomp_block(sqfs_compressor_t *base, const sqfs_u8 *in, - sqfs_u32 size, sqfs_u8 *out, sqfs_u32 outsize) -{ - sqfs_u64 memlimit = 65 * 1024 * 1024; - size_t dest_pos = 0; - size_t src_pos = 0; - lzma_ret ret; - (void)base; - - if (outsize >= 0x7FFFFFFF) - return SQFS_ERROR_ARG_INVALID; - - ret = lzma_stream_buffer_decode(&memlimit, 0, NULL, - in, &src_pos, size, - out, &dest_pos, outsize); - - if (ret == LZMA_OK && size == src_pos) - return dest_pos; - - return SQFS_ERROR_COMPRESSOR; -} - -static void xz_get_configuration(const sqfs_compressor_t *base, - sqfs_compressor_config_t *cfg) -{ - const xz_compressor_t *xz = (const xz_compressor_t *)base; - - memset(cfg, 0, sizeof(*cfg)); - cfg->id = SQFS_COMP_XZ; - cfg->flags = xz->flags; - cfg->block_size = xz->block_size; - cfg->level = xz->level; - cfg->opt.xz.dict_size = xz->dict_size; - cfg->opt.xz.lc = xz->lc; - cfg->opt.xz.lp = xz->lp; - cfg->opt.xz.pb = xz->pb; - - if (base->do_block == xz_uncomp_block) - cfg->flags |= SQFS_COMP_FLAG_UNCOMPRESS; -} - -static sqfs_object_t *xz_create_copy(const sqfs_object_t *cmp) -{ - xz_compressor_t *xz = malloc(sizeof(*xz)); - - if (xz == NULL) - return NULL; - - memcpy(xz, cmp, sizeof(*xz)); - return (sqfs_object_t *)xz; -} - -static void xz_destroy(sqfs_object_t *base) -{ - free(base); -} - -int xz_compressor_create(const sqfs_compressor_config_t *cfg, - sqfs_compressor_t **out) -{ - sqfs_compressor_t *base; - xz_compressor_t *xz; - - if (cfg->flags & ~(SQFS_COMP_FLAG_GENERIC_ALL | - SQFS_COMP_FLAG_XZ_ALL)) { - return SQFS_ERROR_UNSUPPORTED; - } - - if (!is_dict_size_valid(cfg->opt.xz.dict_size)) - return SQFS_ERROR_UNSUPPORTED; - - if (cfg->opt.xz.lc + cfg->opt.xz.lp > 4) - return SQFS_ERROR_UNSUPPORTED; - - if (cfg->opt.xz.pb > SQFS_XZ_MAX_PB) - return SQFS_ERROR_UNSUPPORTED; - - if (cfg->level > SQFS_XZ_MAX_LEVEL) - return SQFS_ERROR_UNSUPPORTED; - - if (cfg->opt.xz.dict_size < SQFS_XZ_MIN_DICT_SIZE) - return SQFS_ERROR_UNSUPPORTED; - - if (cfg->opt.xz.dict_size > SQFS_XZ_MAX_DICT_SIZE) - return SQFS_ERROR_UNSUPPORTED; - - xz = calloc(1, sizeof(*xz)); - base = (sqfs_compressor_t *)xz; - if (xz == NULL) - return SQFS_ERROR_ALLOC; - - sqfs_object_init(xz, xz_destroy, xz_create_copy); - - xz->flags = cfg->flags; - xz->dict_size = cfg->opt.xz.dict_size; - xz->block_size = cfg->block_size; - xz->lc = cfg->opt.xz.lc; - xz->lp = cfg->opt.xz.lp; - xz->pb = cfg->opt.xz.pb; - xz->level = cfg->level; - base->get_configuration = xz_get_configuration; - base->do_block = (cfg->flags & SQFS_COMP_FLAG_UNCOMPRESS) ? - xz_uncomp_block : xz_comp_block; - base->write_options = xz_write_options; - base->read_options = xz_read_options; - - *out = base; - return 0; -} diff --git a/lib/sqfs/comp/zstd.c b/lib/sqfs/comp/zstd.c deleted file mode 100644 index a6d7975..0000000 --- a/lib/sqfs/comp/zstd.c +++ /dev/null @@ -1,172 +0,0 @@ -/* SPDX-License-Identifier: LGPL-3.0-or-later */ -/* - * zstd.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#define SQFS_BUILDING_DLL -#include "config.h" - -#include -#include -#include - -#include -#include - -#include "internal.h" - -typedef struct { - sqfs_compressor_t base; - size_t block_size; - ZSTD_CCtx *zctx; - int level; -} zstd_compressor_t; - -typedef struct { - sqfs_u32 level; -} zstd_options_t; - -static int zstd_write_options(sqfs_compressor_t *base, sqfs_file_t *file) -{ - zstd_compressor_t *zstd = (zstd_compressor_t *)base; - zstd_options_t opt; - - if (zstd->level == SQFS_ZSTD_DEFAULT_LEVEL) - return 0; - - opt.level = htole32(zstd->level); - return sqfs_generic_write_options(file, &opt, sizeof(opt)); -} - -static int zstd_read_options(sqfs_compressor_t *base, sqfs_file_t *file) -{ - zstd_options_t opt; - int ret; - (void)base; - - ret = sqfs_generic_read_options(file, &opt, sizeof(opt)); - if (ret) - return ret; - - opt.level = le32toh(opt.level); - return 0; -} - -static sqfs_s32 zstd_comp_block(sqfs_compressor_t *base, const sqfs_u8 *in, - sqfs_u32 size, sqfs_u8 *out, sqfs_u32 outsize) -{ - zstd_compressor_t *zstd = (zstd_compressor_t *)base; - size_t ret; - - if (size >= 0x7FFFFFFF) - return SQFS_ERROR_ARG_INVALID; - - ret = ZSTD_compressCCtx(zstd->zctx, out, outsize, in, size, - zstd->level); - - if (ZSTD_isError(ret)) { - if (ZSTD_getErrorCode(ret) == ZSTD_error_dstSize_tooSmall) - return 0; - - return SQFS_ERROR_COMPRESSOR; - } - - return ret < size ? ret : 0; -} - -static sqfs_s32 zstd_uncomp_block(sqfs_compressor_t *base, const sqfs_u8 *in, - sqfs_u32 size, sqfs_u8 *out, sqfs_u32 outsize) -{ - size_t ret; - (void)base; - - if (outsize >= 0x7FFFFFFF) - return SQFS_ERROR_ARG_INVALID; - - ret = ZSTD_decompress(out, outsize, in, size); - - if (ZSTD_isError(ret)) - return SQFS_ERROR_COMPRESSOR; - - return ret; -} - -static void zstd_get_configuration(const sqfs_compressor_t *base, - sqfs_compressor_config_t *cfg) -{ - const zstd_compressor_t *zstd = (const zstd_compressor_t *)base; - - memset(cfg, 0, sizeof(*cfg)); - cfg->id = SQFS_COMP_ZSTD; - - cfg->block_size = zstd->block_size; - cfg->level = zstd->level; - - if (base->do_block == zstd_uncomp_block) - cfg->flags |= SQFS_COMP_FLAG_UNCOMPRESS; -} - -static sqfs_object_t *zstd_create_copy(const sqfs_object_t *cmp) -{ - zstd_compressor_t *zstd = malloc(sizeof(*zstd)); - - if (zstd == NULL) - return NULL; - - memcpy(zstd, cmp, sizeof(*zstd)); - - zstd->zctx = ZSTD_createCCtx(); - - if (zstd->zctx == NULL) { - free(zstd); - return NULL; - } - - return (sqfs_object_t *)zstd; -} - -static void zstd_destroy(sqfs_object_t *base) -{ - zstd_compressor_t *zstd = (zstd_compressor_t *)base; - - ZSTD_freeCCtx(zstd->zctx); - free(zstd); -} - -int zstd_compressor_create(const sqfs_compressor_config_t *cfg, - sqfs_compressor_t **out) -{ - zstd_compressor_t *zstd; - sqfs_compressor_t *base; - - if (cfg->flags & ~SQFS_COMP_FLAG_GENERIC_ALL) - return SQFS_ERROR_UNSUPPORTED; - - if (cfg->level < 1 || cfg->level > (unsigned int)ZSTD_maxCLevel()) - return SQFS_ERROR_UNSUPPORTED; - - zstd = calloc(1, sizeof(*zstd)); - base = (sqfs_compressor_t *)zstd; - if (zstd == NULL) - return SQFS_ERROR_ALLOC; - - sqfs_object_init(zstd, zstd_destroy, zstd_create_copy); - - zstd->block_size = cfg->block_size; - zstd->level = cfg->level; - zstd->zctx = ZSTD_createCCtx(); - if (zstd->zctx == NULL) { - free(zstd); - return SQFS_ERROR_COMPRESSOR; - } - - base->get_configuration = zstd_get_configuration; - base->do_block = cfg->flags & SQFS_COMP_FLAG_UNCOMPRESS ? - zstd_uncomp_block : zstd_comp_block; - base->write_options = zstd_write_options; - base->read_options = zstd_read_options; - - *out = base; - return 0; -} diff --git a/lib/sqfs/data_reader.c b/lib/sqfs/data_reader.c deleted file mode 100644 index 3f0cd74..0000000 --- a/lib/sqfs/data_reader.c +++ /dev/null @@ -1,374 +0,0 @@ -/* SPDX-License-Identifier: LGPL-3.0-or-later */ -/* - * data_reader.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#define SQFS_BUILDING_DLL -#include "config.h" - -#include "sqfs/data_reader.h" -#include "sqfs/compressor.h" -#include "sqfs/frag_table.h" -#include "sqfs/block.h" -#include "sqfs/error.h" -#include "sqfs/table.h" -#include "sqfs/inode.h" -#include "sqfs/io.h" -#include "util/util.h" - -#include -#include - -struct sqfs_data_reader_t { - sqfs_object_t obj; - - sqfs_frag_table_t *frag_tbl; - sqfs_compressor_t *cmp; - sqfs_file_t *file; - - sqfs_u8 *data_block; - size_t data_blk_size; - sqfs_u64 current_block; - - sqfs_u8 *frag_block; - size_t frag_blk_size; - sqfs_u32 current_frag_index; - sqfs_u32 block_size; - - sqfs_u8 scratch[]; -}; - -static int get_block(sqfs_data_reader_t *data, sqfs_u64 off, sqfs_u32 size, - sqfs_u32 max_size, size_t *out_sz, sqfs_u8 **out) -{ - sqfs_u32 on_disk_size; - sqfs_s32 ret; - int err; - - *out = alloc_array(1, max_size); - *out_sz = max_size; - - if (*out == NULL) { - err = SQFS_ERROR_ALLOC; - goto fail; - } - - if (SQFS_IS_SPARSE_BLOCK(size)) - return 0; - - on_disk_size = SQFS_ON_DISK_BLOCK_SIZE(size); - - if (on_disk_size > max_size) { - err = SQFS_ERROR_OVERFLOW; - goto fail; - } - - if (SQFS_IS_BLOCK_COMPRESSED(size)) { - err = data->file->read_at(data->file, off, - data->scratch, on_disk_size); - if (err) - goto fail; - - ret = data->cmp->do_block(data->cmp, data->scratch, - on_disk_size, *out, max_size); - if (ret <= 0) { - err = ret < 0 ? ret : SQFS_ERROR_OVERFLOW; - goto fail; - } - - *out_sz = ret; - } else { - err = data->file->read_at(data->file, off, - *out, on_disk_size); - if (err) - goto fail; - - *out_sz = on_disk_size; - } - - return 0; -fail: - free(*out); - *out = NULL; - *out_sz = 0; - return err; -} - -static int precache_data_block(sqfs_data_reader_t *data, sqfs_u64 location, - sqfs_u32 size) -{ - if (data->data_block != NULL && data->current_block == location) - return 0; - - free(data->data_block); - data->current_block = location; - - return get_block(data, location, size, data->block_size, - &data->data_blk_size, &data->data_block); -} - -static int precache_fragment_block(sqfs_data_reader_t *data, size_t idx) -{ - sqfs_fragment_t ent; - int ret; - - if (data->frag_block != NULL && idx == data->current_frag_index) - return 0; - - ret = sqfs_frag_table_lookup(data->frag_tbl, idx, &ent); - if (ret != 0) - return ret; - - free(data->frag_block); - data->current_frag_index = idx; - - return get_block(data, ent.start_offset, ent.size, data->block_size, - &data->frag_blk_size, &data->frag_block); -} - -static void data_reader_destroy(sqfs_object_t *obj) -{ - sqfs_data_reader_t *data = (sqfs_data_reader_t *)obj; - - sqfs_drop(data->cmp); - sqfs_drop(data->file); - sqfs_drop(data->frag_tbl); - free(data->data_block); - free(data->frag_block); - free(data); -} - -static sqfs_object_t *data_reader_copy(const sqfs_object_t *obj) -{ - const sqfs_data_reader_t *data = (const sqfs_data_reader_t *)obj; - sqfs_data_reader_t *copy; - - copy = alloc_flex(sizeof(*data), 1, data->block_size); - if (copy == NULL) - return NULL; - - memcpy(copy, data, sizeof(*data) + data->block_size); - - copy->frag_tbl = sqfs_copy(data->frag_tbl); - if (copy->frag_tbl == NULL) - goto fail_ftbl; - - if (data->data_block != NULL) { - copy->data_block = malloc(data->data_blk_size); - if (copy->data_block == NULL) - goto fail_dblk; - - memcpy(copy->data_block, data->data_block, - data->data_blk_size); - } - - if (copy->frag_block != NULL) { - copy->frag_block = malloc(copy->frag_blk_size); - if (copy->frag_block == NULL) - goto fail_fblk; - - memcpy(copy->frag_block, data->frag_block, - data->frag_blk_size); - } - - /* duplicate references */ - copy->file = sqfs_grab(copy->file); - copy->cmp = sqfs_grab(copy->cmp); - return (sqfs_object_t *)copy; -fail_fblk: - free(copy->data_block); -fail_dblk: - sqfs_drop(copy->frag_tbl); -fail_ftbl: - free(copy); - return NULL; -} - -sqfs_data_reader_t *sqfs_data_reader_create(sqfs_file_t *file, - size_t block_size, - sqfs_compressor_t *cmp, - sqfs_u32 flags) -{ - sqfs_data_reader_t *data; - - if (flags != 0) - return NULL; - - data = alloc_flex(sizeof(*data), 1, block_size); - if (data == NULL) - return NULL; - - sqfs_object_init(data, data_reader_destroy, data_reader_copy); - - data->frag_tbl = sqfs_frag_table_create(0); - if (data->frag_tbl == NULL) { - free(data); - return NULL; - } - - data->file = sqfs_grab(file); - data->block_size = block_size; - data->cmp = sqfs_grab(cmp); - return data; -} - -int sqfs_data_reader_load_fragment_table(sqfs_data_reader_t *data, - const sqfs_super_t *super) -{ - int ret; - - free(data->frag_block); - data->frag_block = NULL; - data->current_frag_index = 0; - - ret = sqfs_frag_table_read(data->frag_tbl, data->file, - super, data->cmp); - if (ret != 0) - return ret; - - data->current_frag_index = sqfs_frag_table_get_size(data->frag_tbl); - return 0; -} - -int sqfs_data_reader_get_block(sqfs_data_reader_t *data, - const sqfs_inode_generic_t *inode, - size_t index, size_t *size, sqfs_u8 **out) -{ - size_t i, unpacked_size; - sqfs_u64 off, filesz; - - sqfs_inode_get_file_block_start(inode, &off); - sqfs_inode_get_file_size(inode, &filesz); - - if (index >= sqfs_inode_get_file_block_count(inode)) - return SQFS_ERROR_OUT_OF_BOUNDS; - - for (i = 0; i < index; ++i) { - off += SQFS_ON_DISK_BLOCK_SIZE(inode->extra[i]); - filesz -= data->block_size; - } - - unpacked_size = filesz < data->block_size ? filesz : data->block_size; - - return get_block(data, off, inode->extra[index], - unpacked_size, size, out); -} - -int sqfs_data_reader_get_fragment(sqfs_data_reader_t *data, - const sqfs_inode_generic_t *inode, - size_t *size, sqfs_u8 **out) -{ - sqfs_u32 frag_idx, frag_off, frag_sz; - size_t block_count; - sqfs_u64 filesz; - int err; - - sqfs_inode_get_file_size(inode, &filesz); - sqfs_inode_get_frag_location(inode, &frag_idx, &frag_off); - *size = 0; - *out = NULL; - - block_count = sqfs_inode_get_file_block_count(inode); - - if (block_count > (UINT64_MAX / data->block_size)) - return SQFS_ERROR_OVERFLOW; - - if ((sqfs_u64)block_count * data->block_size >= filesz) - return 0; - - frag_sz = filesz % data->block_size; - - err = precache_fragment_block(data, frag_idx); - if (err) - return err; - - if (frag_off + frag_sz > data->block_size) - return SQFS_ERROR_OUT_OF_BOUNDS; - - *out = alloc_array(1, frag_sz); - if (*out == NULL) - return SQFS_ERROR_ALLOC; - - *size = frag_sz; - memcpy(*out, (char *)data->frag_block + frag_off, frag_sz); - return 0; -} - -sqfs_s32 sqfs_data_reader_read(sqfs_data_reader_t *data, - const sqfs_inode_generic_t *inode, - sqfs_u64 offset, void *buffer, sqfs_u32 size) -{ - sqfs_u32 frag_idx, frag_off, diff, total = 0; - size_t i, block_count; - sqfs_u64 off, filesz; - char *ptr; - int err; - - if (size >= 0x7FFFFFFF) - size = 0x7FFFFFFE; - - /* work out file location and size */ - sqfs_inode_get_file_size(inode, &filesz); - sqfs_inode_get_frag_location(inode, &frag_idx, &frag_off); - sqfs_inode_get_file_block_start(inode, &off); - block_count = sqfs_inode_get_file_block_count(inode); - - if (offset >= filesz) - return 0; - - if ((filesz - offset) < (sqfs_u64)size) - size = filesz - offset; - - if (size == 0) - return 0; - - /* find location of the first block */ - for (i = 0; offset > data->block_size && i < block_count; ++i) { - off += SQFS_ON_DISK_BLOCK_SIZE(inode->extra[i]); - offset -= data->block_size; - } - - /* copy data from blocks */ - while (i < block_count && size > 0) { - diff = data->block_size - offset; - if (size < diff) - diff = size; - - if (SQFS_IS_SPARSE_BLOCK(inode->extra[i])) { - memset(buffer, 0, diff); - } else { - err = precache_data_block(data, off, inode->extra[i]); - if (err) - return err; - - memcpy(buffer, (char *)data->data_block + offset, diff); - off += SQFS_ON_DISK_BLOCK_SIZE(inode->extra[i]); - } - - ++i; - offset = 0; - size -= diff; - total += diff; - buffer = (char *)buffer + diff; - } - - /* copy from fragment */ - if (size > 0) { - err = precache_fragment_block(data, frag_idx); - if (err) - return err; - - if ((frag_off + offset) >= data->frag_blk_size) - return SQFS_ERROR_OUT_OF_BOUNDS; - - if ((data->frag_blk_size - (frag_off + offset)) < size) - return SQFS_ERROR_OUT_OF_BOUNDS; - - ptr = (char *)data->frag_block + frag_off + offset; - memcpy(buffer, ptr, size); - total += size; - } - - return total; -} diff --git a/lib/sqfs/dir_reader/dir_reader.c b/lib/sqfs/dir_reader/dir_reader.c deleted file mode 100644 index d70f729..0000000 --- a/lib/sqfs/dir_reader/dir_reader.c +++ /dev/null @@ -1,366 +0,0 @@ -/* SPDX-License-Identifier: LGPL-3.0-or-later */ -/* - * fs_reader.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#define SQFS_BUILDING_DLL -#include "internal.h" - -static int inode_copy(const sqfs_inode_generic_t *inode, - sqfs_inode_generic_t **out) -{ - *out = alloc_flex(sizeof(*inode), 1, inode->payload_bytes_used); - if (*out == NULL) - return SQFS_ERROR_ALLOC; - - memcpy(*out, inode, sizeof(*inode) + inode->payload_bytes_used); - return 0; -} - -static int dcache_key_compare(const void *ctx, const void *l, const void *r) -{ - sqfs_u32 lhs = *((const sqfs_u32 *)l), rhs = *((const sqfs_u32 *)r); - (void)ctx; - - return lhs < rhs ? -1 : (lhs > rhs ? 1 : 0); -} - -static int dcache_add(sqfs_dir_reader_t *rd, - const sqfs_inode_generic_t *inode, sqfs_u64 ref) -{ - sqfs_u32 inum = inode->base.inode_number; - - if (!(rd->flags & SQFS_DIR_READER_DOT_ENTRIES)) - return 0; - - if (inode->base.type != SQFS_INODE_DIR && - inode->base.type != SQFS_INODE_EXT_DIR) { - return 0; - } - - if (rbtree_lookup(&rd->dcache, &inum) != NULL) - return 0; - - return rbtree_insert(&rd->dcache, &inum, &ref); -} - -static int dcache_find(sqfs_dir_reader_t *rd, sqfs_u32 inode, sqfs_u64 *ref) -{ - rbtree_node_t *node; - - if (!(rd->flags & SQFS_DIR_READER_DOT_ENTRIES)) - return SQFS_ERROR_NO_ENTRY; - - node = rbtree_lookup(&rd->dcache, &inode); - if (node == NULL) - return SQFS_ERROR_NO_ENTRY; - - *ref = *((sqfs_u64 *)rbtree_node_value(node)); - return 0; -} - -static void dir_reader_destroy(sqfs_object_t *obj) -{ - sqfs_dir_reader_t *rd = (sqfs_dir_reader_t *)obj; - - if (rd->flags & SQFS_DIR_READER_DOT_ENTRIES) - rbtree_cleanup(&rd->dcache); - - sqfs_drop(rd->meta_inode); - sqfs_drop(rd->meta_dir); - free(rd); -} - -static sqfs_object_t *dir_reader_copy(const sqfs_object_t *obj) -{ - const sqfs_dir_reader_t *rd = (const sqfs_dir_reader_t *)obj; - sqfs_dir_reader_t *copy = malloc(sizeof(*copy)); - - if (copy == NULL) - return NULL; - - memcpy(copy, rd, sizeof(*copy)); - - if (rd->flags & SQFS_DIR_READER_DOT_ENTRIES) { - if (rbtree_copy(&rd->dcache, ©->dcache)) - goto fail_cache; - } - - copy->meta_inode = sqfs_copy(rd->meta_inode); - if (copy->meta_inode == NULL) - goto fail_mino; - - copy->meta_dir = sqfs_copy(rd->meta_dir); - if (copy->meta_dir == NULL) - goto fail_mdir; - - return (sqfs_object_t *)copy; -fail_mdir: - sqfs_drop(copy->meta_inode); -fail_mino: - if (copy->flags & SQFS_DIR_READER_DOT_ENTRIES) - rbtree_cleanup(©->dcache); -fail_cache: - free(copy); - return NULL; -} - -sqfs_dir_reader_t *sqfs_dir_reader_create(const sqfs_super_t *super, - sqfs_compressor_t *cmp, - sqfs_file_t *file, - sqfs_u32 flags) -{ - sqfs_dir_reader_t *rd; - sqfs_u64 start, limit; - int ret; - - if (flags & ~SQFS_DIR_READER_ALL_FLAGS) - return NULL; - - rd = calloc(1, sizeof(*rd)); - if (rd == NULL) - return NULL; - - sqfs_object_init(rd, dir_reader_destroy, dir_reader_copy); - - if (flags & SQFS_DIR_READER_DOT_ENTRIES) { - ret = rbtree_init(&rd->dcache, sizeof(sqfs_u32), - sizeof(sqfs_u64), dcache_key_compare); - - if (ret != 0) - goto fail_dcache; - } - - start = super->inode_table_start; - limit = super->directory_table_start; - - rd->meta_inode = sqfs_meta_reader_create(file, cmp, start, limit); - if (rd->meta_inode == NULL) - goto fail_mino; - - start = super->directory_table_start; - limit = super->id_table_start; - - if (super->fragment_table_start < limit) - limit = super->fragment_table_start; - - if (super->export_table_start < limit) - limit = super->export_table_start; - - rd->meta_dir = sqfs_meta_reader_create(file, cmp, start, limit); - if (rd->meta_dir == NULL) - goto fail_mdir; - - rd->super = *super; - rd->flags = flags; - rd->state = DIR_STATE_NONE; - return rd; -fail_mdir: - sqfs_drop(rd->meta_inode); -fail_mino: - if (flags & SQFS_DIR_READER_DOT_ENTRIES) - rbtree_cleanup(&rd->dcache); -fail_dcache: - free(rd); - return NULL; -} - -int sqfs_dir_reader_open_dir(sqfs_dir_reader_t *rd, - const sqfs_inode_generic_t *inode, - sqfs_u32 flags) -{ - sqfs_u32 parent; - int ret; - - if (flags & (~SQFS_DIR_OPEN_ALL_FLAGS)) - return SQFS_ERROR_UNSUPPORTED; - - ret = sqfs_readdir_state_init(&rd->it, &rd->super, inode); - if (ret) - return ret; - - if ((rd->flags & SQFS_DIR_READER_DOT_ENTRIES) && - !(flags & SQFS_DIR_OPEN_NO_DOT_ENTRIES)) { - if (inode->base.type == SQFS_INODE_EXT_DIR) { - parent = inode->data.dir_ext.parent_inode; - } else { - parent = inode->data.dir.parent_inode; - } - - if (dcache_find(rd, inode->base.inode_number, &rd->cur_ref)) - return SQFS_ERROR_NO_ENTRY; - - if (rd->cur_ref == rd->super.root_inode_ref) { - rd->parent_ref = rd->cur_ref; - } else if (dcache_find(rd, parent, &rd->parent_ref)) { - return SQFS_ERROR_NO_ENTRY; - } - - rd->state = DIR_STATE_OPENED; - } else { - rd->state = DIR_STATE_ENTRIES; - } - - rd->start_state = rd->state; - return 0; -} - -static int mk_dummy_entry(const char *str, sqfs_dir_entry_t **out) -{ - size_t len = strlen(str); - sqfs_dir_entry_t *ent; - - ent = calloc(1, sizeof(sqfs_dir_entry_t) + len + 1); - if (ent == NULL) - return SQFS_ERROR_ALLOC; - - ent->type = SQFS_INODE_DIR; - ent->size = len - 1; - - strcpy((char *)ent->name, str); - - *out = ent; - return 0; -} - -int sqfs_dir_reader_read(sqfs_dir_reader_t *rd, sqfs_dir_entry_t **out) -{ - int err; - - switch (rd->state) { - case DIR_STATE_OPENED: - err = mk_dummy_entry(".", out); - if (err == 0) { - rd->state = DIR_STATE_DOT; - rd->ent_ref = rd->cur_ref; - } - return err; - case DIR_STATE_DOT: - err = mk_dummy_entry("..", out); - if (err == 0) { - rd->state = DIR_STATE_ENTRIES; - rd->ent_ref = rd->parent_ref; - } - return err; - case DIR_STATE_ENTRIES: - break; - default: - return SQFS_ERROR_SEQUENCE; - } - - return sqfs_meta_reader_readdir(rd->meta_dir, &rd->it, - out, NULL, &rd->ent_ref); -} - -int sqfs_dir_reader_rewind(sqfs_dir_reader_t *rd) -{ - if (rd->state == DIR_STATE_NONE) - return SQFS_ERROR_SEQUENCE; - - sqfs_readdir_state_reset(&rd->it); - rd->state = rd->start_state; - return 0; -} - -int sqfs_dir_reader_find(sqfs_dir_reader_t *rd, const char *name) -{ - sqfs_dir_entry_t *ent; - int ret; - - ret = sqfs_dir_reader_rewind(rd); - if (ret != 0) - return ret; - - do { - ret = sqfs_dir_reader_read(rd, &ent); - if (ret < 0) - return ret; - if (ret > 0) - return SQFS_ERROR_NO_ENTRY; - - ret = strcmp((const char *)ent->name, name); - free(ent); - } while (ret < 0); - - return ret == 0 ? 0 : SQFS_ERROR_NO_ENTRY; -} - -int sqfs_dir_reader_get_inode(sqfs_dir_reader_t *rd, - sqfs_inode_generic_t **inode) -{ - int ret; - - ret = sqfs_meta_reader_read_inode(rd->meta_inode, &rd->super, - rd->ent_ref >> 16, - rd->ent_ref & 0x0FFFF, inode); - if (ret != 0) - return ret; - - return dcache_add(rd, *inode, rd->ent_ref); -} - -int sqfs_dir_reader_get_root_inode(sqfs_dir_reader_t *rd, - sqfs_inode_generic_t **inode) -{ - sqfs_u64 block_start = rd->super.root_inode_ref >> 16; - sqfs_u16 offset = rd->super.root_inode_ref & 0xFFFF; - int ret; - - ret = sqfs_meta_reader_read_inode(rd->meta_inode, &rd->super, - block_start, offset, inode); - if (ret != 0) - return ret; - - return dcache_add(rd, *inode, rd->super.root_inode_ref); -} - -int sqfs_dir_reader_find_by_path(sqfs_dir_reader_t *rd, - const sqfs_inode_generic_t *start, - const char *path, sqfs_inode_generic_t **out) -{ - sqfs_inode_generic_t *inode; - const char *ptr; - int ret = 0; - char *name; - - if (start == NULL) { - ret = sqfs_dir_reader_get_root_inode(rd, &inode); - } else { - ret = inode_copy(start, &inode); - } - - if (ret) - return ret; - - for (; *path != '\0'; path = ptr) { - if (*path == '/') { - for (ptr = path; *ptr == '/'; ++ptr) - ; - continue; - } - - ret = sqfs_dir_reader_open_dir(rd, inode, 0); - free(inode); - if (ret) - return ret; - - ptr = strchrnul(path, '/'); - - name = strndup(path, ptr - path); - if (name == NULL) - return SQFS_ERROR_ALLOC; - - ret = sqfs_dir_reader_find(rd, name); - free(name); - if (ret) - return ret; - - ret = sqfs_dir_reader_get_inode(rd, &inode); - if (ret) - return ret; - } - - *out = inode; - return 0; -} diff --git a/lib/sqfs/dir_reader/get_path.c b/lib/sqfs/dir_reader/get_path.c deleted file mode 100644 index 847bfd3..0000000 --- a/lib/sqfs/dir_reader/get_path.c +++ /dev/null @@ -1,81 +0,0 @@ -/* SPDX-License-Identifier: LGPL-3.0-or-later */ -/* - * get_path.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#define SQFS_BUILDING_DLL -#include "internal.h" - -#include -#include - -int sqfs_tree_node_get_path(const sqfs_tree_node_t *node, char **out) -{ - const sqfs_tree_node_t *it; - size_t clen, len = 0; - char *str, *ptr; - - *out = NULL; - - if (node == NULL) - return SQFS_ERROR_ARG_INVALID; - - for (it = node; it->parent != NULL; it = it->parent) { - if (it->parent == node) - return SQFS_ERROR_LINK_LOOP; - - /* non-root nodes must have a valid name */ - clen = strlen((const char *)it->name); - - if (clen == 0) - return SQFS_ERROR_CORRUPTED; - - if (strchr((const char *)it->name, '/') != NULL) - return SQFS_ERROR_CORRUPTED; - - if (it->name[0] == '.') { - if (clen == 1 || (clen == 2 && it->name[1] == '.')) - return SQFS_ERROR_CORRUPTED; - } - - /* compute total path length */ - if (SZ_ADD_OV(clen, 1, &clen)) - return SQFS_ERROR_OVERFLOW; - - if (SZ_ADD_OV(len, clen, &len)) - return SQFS_ERROR_OVERFLOW; - } - - /* root node must not have a name */ - if (it->name[0] != '\0') - return SQFS_ERROR_ARG_INVALID; - - /* generate the path */ - if (node->parent == NULL) { - str = strdup("/"); - if (str == NULL) - return SQFS_ERROR_ALLOC; - } else { - if (SZ_ADD_OV(len, 1, &len)) - return SQFS_ERROR_OVERFLOW; - - str = malloc(len); - if (str == NULL) - return SQFS_ERROR_ALLOC; - - ptr = str + len - 1; - *ptr = '\0'; - - for (it = node; it->parent != NULL; it = it->parent) { - len = strlen((const char *)it->name); - ptr -= len; - - memcpy(ptr, (const char *)it->name, len); - *(--ptr) = '/'; - } - } - - *out = str; - return 0; -} diff --git a/lib/sqfs/dir_reader/internal.h b/lib/sqfs/dir_reader/internal.h deleted file mode 100644 index 471d197..0000000 --- a/lib/sqfs/dir_reader/internal.h +++ /dev/null @@ -1,52 +0,0 @@ -/* SPDX-License-Identifier: LGPL-3.0-or-later */ -/* - * internal.h - * - * Copyright (C) 2019 David Oberhollenzer - */ -#ifndef DIR_READER_INTERNAL_H -#define DIR_READER_INTERNAL_H - -#include "config.h" - -#include "sqfs/meta_reader.h" -#include "sqfs/dir_reader.h" -#include "sqfs/compressor.h" -#include "sqfs/id_table.h" -#include "sqfs/super.h" -#include "sqfs/inode.h" -#include "sqfs/error.h" -#include "sqfs/dir.h" -#include "util/rbtree.h" -#include "util/util.h" - -#include -#include - -enum { - DIR_STATE_NONE = 0, - DIR_STATE_OPENED = 1, - DIR_STATE_DOT = 2, - DIR_STATE_ENTRIES = 3, -}; - -struct sqfs_dir_reader_t { - sqfs_object_t base; - - sqfs_meta_reader_t *meta_dir; - sqfs_meta_reader_t *meta_inode; - sqfs_super_t super; - - sqfs_readdir_state_t it; - - sqfs_u32 flags; - - int start_state; - int state; - sqfs_u64 parent_ref; - sqfs_u64 cur_ref; - sqfs_u64 ent_ref; - rbtree_t dcache; -}; - -#endif /* DIR_READER_INTERNAL_H */ diff --git a/lib/sqfs/dir_reader/read_tree.c b/lib/sqfs/dir_reader/read_tree.c deleted file mode 100644 index 91cc2c0..0000000 --- a/lib/sqfs/dir_reader/read_tree.c +++ /dev/null @@ -1,288 +0,0 @@ -/* SPDX-License-Identifier: LGPL-3.0-or-later */ -/* - * read_tree.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#define SQFS_BUILDING_DLL -#include "internal.h" - -static int should_skip(int type, unsigned int flags) -{ - switch (type) { - case SQFS_INODE_BDEV: - case SQFS_INODE_CDEV: - case SQFS_INODE_EXT_CDEV: - case SQFS_INODE_EXT_BDEV: - return (flags & SQFS_TREE_NO_DEVICES); - case SQFS_INODE_SLINK: - case SQFS_INODE_EXT_SLINK: - return (flags & SQFS_TREE_NO_SLINKS); - case SQFS_INODE_SOCKET: - case SQFS_INODE_EXT_SOCKET: - return(flags & SQFS_TREE_NO_SOCKETS); - case SQFS_INODE_FIFO: - case SQFS_INODE_EXT_FIFO: - return (flags & SQFS_TREE_NO_FIFO); - default: - break; - } - - return 0; -} - -static bool would_be_own_parent(sqfs_tree_node_t *parent, sqfs_tree_node_t *n) -{ - sqfs_u32 inum = n->inode->base.inode_number; - - while (parent != NULL) { - if (parent->inode->base.inode_number == inum) - return true; - - parent = parent->parent; - } - - return false; -} - -static sqfs_tree_node_t *create_node(sqfs_inode_generic_t *inode, - const char *name) -{ - sqfs_tree_node_t *n; - - n = alloc_flex(sizeof(*n), 1, strlen(name) + 1); - if (n == NULL) - return NULL; - - n->inode = inode; - strcpy((char *)n->name, name); - return n; -} - -static int fill_dir(sqfs_dir_reader_t *dr, sqfs_tree_node_t *root, - unsigned int flags) -{ - sqfs_tree_node_t *n, *prev, **tail; - sqfs_inode_generic_t *inode; - sqfs_dir_entry_t *ent; - int err; - - tail = &root->children; - - for (;;) { - err = sqfs_dir_reader_read(dr, &ent); - if (err > 0) - break; - if (err < 0) - return err; - - if (should_skip(ent->type, flags)) { - free(ent); - continue; - } - - err = sqfs_dir_reader_get_inode(dr, &inode); - if (err) { - free(ent); - return err; - } - - n = create_node(inode, (const char *)ent->name); - free(ent); - - if (n == NULL) { - free(inode); - return SQFS_ERROR_ALLOC; - } - - if (would_be_own_parent(root, n)) { - free(n); - free(inode); - return SQFS_ERROR_LINK_LOOP; - } - - *tail = n; - tail = &n->next; - n->parent = root; - } - - n = root->children; - prev = NULL; - - while (n != NULL) { - if (n->inode->base.type == SQFS_INODE_DIR || - n->inode->base.type == SQFS_INODE_EXT_DIR) { - if (!(flags & SQFS_TREE_NO_RECURSE)) { - err = sqfs_dir_reader_open_dir(dr, n->inode, - SQFS_DIR_OPEN_NO_DOT_ENTRIES); - if (err) - return err; - - err = fill_dir(dr, n, flags); - if (err) - return err; - } - - if (n->children == NULL && - (flags & SQFS_TREE_NO_EMPTY)) { - free(n->inode); - if (prev == NULL) { - root->children = root->children->next; - free(n); - n = root->children; - } else { - prev->next = n->next; - free(n); - n = prev->next; - } - continue; - } - } - - prev = n; - n = n->next; - } - - return 0; -} - -static int resolve_ids(sqfs_tree_node_t *root, const sqfs_id_table_t *idtbl) -{ - sqfs_tree_node_t *it; - int err; - - for (it = root->children; it != NULL; it = it->next) - resolve_ids(it, idtbl); - - err = sqfs_id_table_index_to_id(idtbl, root->inode->base.uid_idx, - &root->uid); - if (err) - return err; - - return sqfs_id_table_index_to_id(idtbl, root->inode->base.gid_idx, - &root->gid); -} - -void sqfs_dir_tree_destroy(sqfs_tree_node_t *root) -{ - sqfs_tree_node_t *it; - - if (!root) - return; - - while (root->children != NULL) { - it = root->children; - root->children = it->next; - - sqfs_dir_tree_destroy(it); - } - - free(root->inode); - free(root); -} - -int sqfs_dir_reader_get_full_hierarchy(sqfs_dir_reader_t *rd, - const sqfs_id_table_t *idtbl, - const char *path, unsigned int flags, - sqfs_tree_node_t **out) -{ - sqfs_tree_node_t *root, *tail, *new; - sqfs_inode_generic_t *inode; - sqfs_dir_entry_t *ent; - const char *ptr; - int ret; - - if (flags & ~SQFS_TREE_ALL_FLAGS) - return SQFS_ERROR_UNSUPPORTED; - - ret = sqfs_dir_reader_get_root_inode(rd, &inode); - if (ret) - return ret; - - root = tail = create_node(inode, ""); - if (root == NULL) { - free(inode); - return SQFS_ERROR_ALLOC; - } - inode = NULL; - - while (path != NULL && *path != '\0') { - if (*path == '/') { - while (*path == '/') - ++path; - continue; - } - - ret = sqfs_dir_reader_open_dir(rd, tail->inode, - SQFS_DIR_OPEN_NO_DOT_ENTRIES); - if (ret) - goto fail; - - ptr = strchrnul(path, '/'); - - for (;;) { - ret = sqfs_dir_reader_read(rd, &ent); - if (ret < 0) - goto fail; - if (ret > 0) { - ret = SQFS_ERROR_NO_ENTRY; - goto fail; - } - - ret = strncmp((const char *)ent->name, - path, ptr - path); - if (ret == 0 && ent->name[ptr - path] == '\0') - break; - free(ent); - } - - ret = sqfs_dir_reader_get_inode(rd, &inode); - if (ret) { - free(ent); - goto fail; - } - - new = create_node(inode, (const char *)ent->name); - free(ent); - - if (new == NULL) { - free(inode); - ret = SQFS_ERROR_ALLOC; - goto fail; - } - - inode = NULL; - path = ptr; - - if (flags & SQFS_TREE_STORE_PARENTS) { - tail->children = new; - new->parent = tail; - tail = new; - } else { - sqfs_dir_tree_destroy(root); - root = tail = new; - } - } - - if (tail->inode->base.type == SQFS_INODE_DIR || - tail->inode->base.type == SQFS_INODE_EXT_DIR) { - ret = sqfs_dir_reader_open_dir(rd, tail->inode, - SQFS_DIR_OPEN_NO_DOT_ENTRIES); - if (ret) - goto fail; - - ret = fill_dir(rd, tail, flags); - if (ret) - goto fail; - } - - ret = resolve_ids(root, idtbl); - if (ret) - goto fail; - - *out = root; - return 0; -fail: - sqfs_dir_tree_destroy(root); - return ret; -} diff --git a/lib/sqfs/dir_writer.c b/lib/sqfs/dir_writer.c deleted file mode 100644 index d2b72df..0000000 --- a/lib/sqfs/dir_writer.c +++ /dev/null @@ -1,460 +0,0 @@ -/* SPDX-License-Identifier: LGPL-3.0-or-later */ -/* - * dir_writer.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#define SQFS_BUILDING_DLL -#include "config.h" - -#include "sqfs/meta_writer.h" -#include "sqfs/dir_writer.h" -#include "sqfs/super.h" -#include "sqfs/table.h" -#include "sqfs/inode.h" -#include "sqfs/error.h" -#include "sqfs/block.h" -#include "sqfs/dir.h" -#include "util/array.h" -#include "util/util.h" - -#include -#include - -#define DIR_INDEX_THRESHOLD (256) - -typedef struct dir_entry_t { - struct dir_entry_t *next; - sqfs_u64 inode_ref; - sqfs_u32 inode_num; - sqfs_u16 type; - size_t name_len; - char name[]; -} dir_entry_t; - -typedef struct index_ent_t { - struct index_ent_t *next; - dir_entry_t *ent; - sqfs_u64 block; - sqfs_u32 index; -} index_ent_t; - -struct sqfs_dir_writer_t { - sqfs_object_t base; - - dir_entry_t *list; - dir_entry_t *list_end; - - index_ent_t *idx; - index_ent_t *idx_end; - - sqfs_u64 dir_ref; - size_t dir_size; - size_t ent_count; - sqfs_meta_writer_t *dm; - - array_t export_tbl; -}; - -static int get_type(sqfs_u16 mode) -{ - switch (mode & S_IFMT) { - case S_IFSOCK: return SQFS_INODE_SOCKET; - case S_IFIFO: return SQFS_INODE_FIFO; - case S_IFLNK: return SQFS_INODE_SLINK; - case S_IFBLK: return SQFS_INODE_BDEV; - case S_IFCHR: return SQFS_INODE_CDEV; - case S_IFDIR: return SQFS_INODE_DIR; - case S_IFREG: return SQFS_INODE_FILE; - default: - break; - } - - return SQFS_ERROR_UNSUPPORTED; -} - -static void writer_reset(sqfs_dir_writer_t *writer) -{ - dir_entry_t *ent; - index_ent_t *idx; - - while (writer->idx != NULL) { - idx = writer->idx; - writer->idx = idx->next; - free(idx); - } - - while (writer->list != NULL) { - ent = writer->list; - writer->list = ent->next; - free(ent); - } - - writer->list_end = NULL; - writer->idx_end = NULL; - writer->dir_ref = 0; - writer->dir_size = 0; - writer->ent_count = 0; -} - -static int add_export_table_entry(sqfs_dir_writer_t *writer, - sqfs_u32 inum, sqfs_u64 iref) -{ - sqfs_u64 *ptr; - int ret; - - if (writer->export_tbl.data == NULL) - return 0; - - if (inum < 1) - return SQFS_ERROR_ARG_INVALID; - - ret = array_set_capacity(&writer->export_tbl, inum); - if (ret != 0) - return ret; - - ptr = (sqfs_u64 *)writer->export_tbl.data; - - if ((inum - 1) >= writer->export_tbl.used) { - memset(ptr + writer->export_tbl.used, 0xFF, - (inum - writer->export_tbl.used) * sizeof(*ptr)); - - writer->export_tbl.used = inum; - } - - ptr[inum - 1] = iref; - return 0; -} - -static void dir_writer_destroy(sqfs_object_t *obj) -{ - sqfs_dir_writer_t *writer = (sqfs_dir_writer_t *)obj; - - sqfs_drop(writer->dm); - writer_reset(writer); - array_cleanup(&writer->export_tbl); - free(writer); -} - -sqfs_dir_writer_t *sqfs_dir_writer_create(sqfs_meta_writer_t *dm, - sqfs_u32 flags) -{ - sqfs_dir_writer_t *writer; - - if (flags & ~SQFS_DIR_WRITER_CREATE_ALL_FLAGS) - return NULL; - - writer = calloc(1, sizeof(*writer)); - if (writer == NULL) - return NULL; - - sqfs_object_init(writer, dir_writer_destroy, NULL); - - if (flags & SQFS_DIR_WRITER_CREATE_EXPORT_TABLE) { - if (array_init(&writer->export_tbl, sizeof(sqfs_u64), 512)) { - free(writer); - return NULL; - } - - memset(writer->export_tbl.data, 0xFF, - writer->export_tbl.size * writer->export_tbl.count); - } - - writer->dm = sqfs_grab(dm); - return writer; -} - -int sqfs_dir_writer_begin(sqfs_dir_writer_t *writer, sqfs_u32 flags) -{ - sqfs_u32 offset; - sqfs_u64 block; - - if (flags != 0) - return SQFS_ERROR_UNSUPPORTED; - - writer_reset(writer); - - sqfs_meta_writer_get_position(writer->dm, &block, &offset); - writer->dir_ref = (block << 16) | offset; - return 0; -} - -int sqfs_dir_writer_add_entry(sqfs_dir_writer_t *writer, const char *name, - sqfs_u32 inode_num, sqfs_u64 inode_ref, - sqfs_u16 mode) -{ - dir_entry_t *ent; - int type, err; - - type = get_type(mode); - if (type < 0) - return type; - - if (name[0] == '\0' || inode_num < 1) - return SQFS_ERROR_ARG_INVALID; - - err = add_export_table_entry(writer, inode_num, inode_ref); - if (err) - return err; - - ent = alloc_flex(sizeof(*ent), 1, strlen(name)); - if (ent == NULL) - return SQFS_ERROR_ALLOC; - - ent->inode_ref = inode_ref; - ent->inode_num = inode_num; - ent->type = type; - ent->name_len = strlen(name); - memcpy(ent->name, name, ent->name_len); - - if (writer->list_end == NULL) { - writer->list = writer->list_end = ent; - } else { - writer->list_end->next = ent; - writer->list_end = ent; - } - - writer->ent_count += 1; - return 0; -} - -static size_t get_conseq_entry_count(sqfs_u32 offset, dir_entry_t *head) -{ - size_t size, count = 0; - dir_entry_t *it; - sqfs_s32 diff; - - size = (offset + sizeof(sqfs_dir_header_t)) % SQFS_META_BLOCK_SIZE; - - for (it = head; it != NULL; it = it->next) { - if ((it->inode_ref >> 16) != (head->inode_ref >> 16)) - break; - - diff = it->inode_num - head->inode_num; - - if (diff > 32767 || diff < -32767) - break; - - size += sizeof(sqfs_dir_entry_t) + it->name_len; - - if (count > 0 && size > SQFS_META_BLOCK_SIZE) - break; - - count += 1; - - if (count == SQFS_MAX_DIR_ENT) - break; - } - - return count; -} - -static int add_header(sqfs_dir_writer_t *writer, size_t count, - dir_entry_t *ref, sqfs_u64 block) -{ - sqfs_dir_header_t hdr; - index_ent_t *idx; - int err; - - hdr.count = htole32(count - 1); - hdr.start_block = htole32(ref->inode_ref >> 16); - hdr.inode_number = htole32(ref->inode_num); - - err = sqfs_meta_writer_append(writer->dm, &hdr, sizeof(hdr)); - if (err) - return err; - - idx = calloc(1, sizeof(*idx)); - if (idx == NULL) - return SQFS_ERROR_ALLOC; - - idx->ent = ref; - idx->block = block; - idx->index = writer->dir_size; - - if (writer->idx_end == NULL) { - writer->idx = writer->idx_end = idx; - } else { - writer->idx_end->next = idx; - writer->idx_end = idx; - } - - writer->dir_size += sizeof(hdr); - return 0; -} - -int sqfs_dir_writer_end(sqfs_dir_writer_t *writer) -{ - dir_entry_t *it, *first; - sqfs_dir_entry_t ent; - sqfs_u16 *diff_u16; - size_t i, count; - sqfs_u32 offset; - sqfs_u64 block; - int err; - - for (it = writer->list; it != NULL; ) { - sqfs_meta_writer_get_position(writer->dm, &block, &offset); - count = get_conseq_entry_count(offset, it); - - err = add_header(writer, count, it, block); - if (err) - return err; - - first = it; - - for (i = 0; i < count; ++i) { - ent.offset = htole16(it->inode_ref & 0x0000FFFF); - ent.inode_diff = it->inode_num - first->inode_num; - ent.type = htole16(it->type); - ent.size = htole16(it->name_len - 1); - - diff_u16 = (sqfs_u16 *)&ent.inode_diff; - *diff_u16 = htole16(*diff_u16); - - err = sqfs_meta_writer_append(writer->dm, &ent, - sizeof(ent)); - if (err) - return err; - - err = sqfs_meta_writer_append(writer->dm, it->name, - it->name_len); - if (err) - return err; - - writer->dir_size += sizeof(ent) + it->name_len; - it = it->next; - } - } - - return 0; -} - -size_t sqfs_dir_writer_get_size(const sqfs_dir_writer_t *writer) -{ - return writer->dir_size; -} - -sqfs_u64 sqfs_dir_writer_get_dir_reference(const sqfs_dir_writer_t *writer) -{ - return writer->dir_ref; -} - -size_t sqfs_dir_writer_get_index_size(const sqfs_dir_writer_t *writer) -{ - size_t index_size = 0; - index_ent_t *idx; - - for (idx = writer->idx; idx != NULL; idx = idx->next) - index_size += sizeof(sqfs_dir_index_t) + idx->ent->name_len; - - return index_size; -} - -size_t sqfs_dir_writer_get_entry_count(const sqfs_dir_writer_t *writer) -{ - return writer->ent_count; -} - -sqfs_inode_generic_t -*sqfs_dir_writer_create_inode(const sqfs_dir_writer_t *writer, - size_t hlinks, sqfs_u32 xattr, - sqfs_u32 parent_ino) -{ - sqfs_inode_generic_t *inode; - sqfs_dir_index_t ent; - sqfs_u64 start_block; - sqfs_u16 block_offset; - size_t index_size; - index_ent_t *idx; - sqfs_u8 *ptr; - - index_size = 0; - - for (idx = writer->idx; idx != NULL; idx = idx->next) - index_size += sizeof(ent) + idx->ent->name_len; - - inode = alloc_flex(sizeof(*inode), 1, index_size); - if (inode == NULL) - return NULL; - - inode->payload_bytes_available = index_size; - start_block = writer->dir_ref >> 16; - block_offset = writer->dir_ref & 0xFFFF; - - if (xattr != 0xFFFFFFFF || start_block > 0xFFFFFFFFUL || - writer->dir_size > (0xFFFF - 3)) { - inode->base.type = SQFS_INODE_EXT_DIR; - } else { - inode->base.type = SQFS_INODE_DIR; - } - - if (writer->ent_count >= DIR_INDEX_THRESHOLD) - inode->base.type = SQFS_INODE_EXT_DIR; - - if (inode->base.type == SQFS_INODE_DIR) { - inode->data.dir.start_block = start_block; - inode->data.dir.nlink = writer->ent_count + hlinks + 2; - inode->data.dir.size = writer->dir_size + 3; - inode->data.dir.offset = block_offset; - inode->data.dir.parent_inode = parent_ino; - } else { - inode->data.dir_ext.nlink = writer->ent_count + hlinks + 2; - inode->data.dir_ext.size = writer->dir_size + 3; - inode->data.dir_ext.start_block = start_block; - inode->data.dir_ext.parent_inode = parent_ino; - inode->data.dir_ext.offset = block_offset; - inode->data.dir_ext.xattr_idx = xattr; - inode->data.dir_ext.inodex_count = 0; - inode->payload_bytes_used = 0; - - for (idx = writer->idx; idx != NULL; idx = idx->next) { - memset(&ent, 0, sizeof(ent)); - ent.start_block = idx->block; - ent.index = idx->index; - ent.size = idx->ent->name_len - 1; - - ptr = (sqfs_u8 *)inode->extra + - inode->payload_bytes_used; - memcpy(ptr, &ent, sizeof(ent)); - memcpy(ptr + sizeof(ent), idx->ent->name, - idx->ent->name_len); - - inode->data.dir_ext.inodex_count += 1; - inode->payload_bytes_used += sizeof(ent); - inode->payload_bytes_used += idx->ent->name_len; - } - } - - return inode; -} - -int sqfs_dir_writer_write_export_table(sqfs_dir_writer_t *writer, - sqfs_file_t *file, - sqfs_compressor_t *cmp, - sqfs_u32 root_inode_num, - sqfs_u64 root_inode_ref, - sqfs_super_t *super) -{ - sqfs_u64 start; - size_t size; - int ret; - - ret = add_export_table_entry(writer, root_inode_num, root_inode_ref); - if (ret) - return 0; - - if (writer->export_tbl.data == NULL) - return 0; - - size = writer->export_tbl.size * writer->export_tbl.used; - - ret = sqfs_write_table(file, cmp, writer->export_tbl.data, - size, &start); - if (ret) - return ret; - - super->export_table_start = start; - super->flags |= SQFS_FLAG_EXPORTABLE; - return 0; -} diff --git a/lib/sqfs/frag_table.c b/lib/sqfs/frag_table.c deleted file mode 100644 index 151df28..0000000 --- a/lib/sqfs/frag_table.c +++ /dev/null @@ -1,204 +0,0 @@ -/* SPDX-License-Identifier: LGPL-3.0-or-later */ -/* - * frag_table.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#define SQFS_BUILDING_DLL -#include "config.h" - -#include "sqfs/frag_table.h" -#include "sqfs/super.h" -#include "sqfs/table.h" -#include "sqfs/error.h" -#include "sqfs/block.h" -#include "compat.h" -#include "util/array.h" - -#include -#include - -struct sqfs_frag_table_t { - sqfs_object_t base; - - array_t table; -}; - -static void frag_table_destroy(sqfs_object_t *obj) -{ - sqfs_frag_table_t *tbl = (sqfs_frag_table_t *)obj; - - array_cleanup(&tbl->table); - free(tbl); -} - -static sqfs_object_t *frag_table_copy(const sqfs_object_t *obj) -{ - const sqfs_frag_table_t *tbl = (const sqfs_frag_table_t *)obj; - sqfs_frag_table_t *copy = calloc(1, sizeof(*copy)); - - if (copy == NULL) - return NULL; - - if (array_init_copy(©->table, &tbl->table)) { - free(copy); - return NULL; - } - - return (sqfs_object_t *)copy; -} - -sqfs_frag_table_t *sqfs_frag_table_create(sqfs_u32 flags) -{ - sqfs_frag_table_t *tbl; - - if (flags != 0) - return NULL; - - tbl = calloc(1, sizeof(*tbl)); - if (tbl == NULL) - return NULL; - - sqfs_object_init(tbl, frag_table_destroy, frag_table_copy); - - array_init(&tbl->table, sizeof(sqfs_fragment_t), 0); - return tbl; -} - -int sqfs_frag_table_read(sqfs_frag_table_t *tbl, sqfs_file_t *file, - const sqfs_super_t *super, sqfs_compressor_t *cmp) -{ - sqfs_u64 location, lower, upper; - void *raw = NULL; - size_t size; - int err; - - array_cleanup(&tbl->table); - tbl->table.size = sizeof(sqfs_fragment_t); - - if (super->flags & SQFS_FLAG_NO_FRAGMENTS) - return 0; - - if (super->fragment_table_start == 0xFFFFFFFFFFFFFFFFUL) - return 0; - - if (super->fragment_entry_count == 0) - return 0; - - if (super->fragment_table_start >= super->bytes_used) - return SQFS_ERROR_OUT_OF_BOUNDS; - - /* location must be after inode & directory table, - but before the ID table */ - if (super->fragment_table_start < super->directory_table_start) - return SQFS_ERROR_CORRUPTED; - - if (super->fragment_table_start >= super->id_table_start) - return SQFS_ERROR_CORRUPTED; - - location = super->fragment_table_start; - lower = super->directory_table_start; - upper = super->id_table_start; - - if (super->export_table_start < super->id_table_start) - upper = super->export_table_start; - - if (SZ_MUL_OV(super->fragment_entry_count, sizeof(sqfs_fragment_t), - &size)) { - return SQFS_ERROR_OVERFLOW; - } - - err = sqfs_read_table(file, cmp, size, location, lower, upper, &raw); - if (err) { - free(raw); - return err; - } - - tbl->table.data = raw; - tbl->table.count = super->fragment_entry_count; - tbl->table.used = super->fragment_entry_count; - return 0; -} - -int sqfs_frag_table_write(sqfs_frag_table_t *tbl, sqfs_file_t *file, - sqfs_super_t *super, sqfs_compressor_t *cmp) -{ - size_t i; - int err; - - if (tbl->table.used == 0) { - super->fragment_table_start = 0xFFFFFFFFFFFFFFFF; - super->flags |= SQFS_FLAG_NO_FRAGMENTS; - super->flags &= ~SQFS_FLAG_ALWAYS_FRAGMENTS; - super->flags &= ~SQFS_FLAG_UNCOMPRESSED_FRAGMENTS; - return 0; - } - - err = sqfs_write_table(file, cmp, tbl->table.data, - tbl->table.size * tbl->table.used, - &super->fragment_table_start); - if (err) - return err; - - super->fragment_entry_count = tbl->table.used; - super->flags &= ~SQFS_FLAG_NO_FRAGMENTS; - super->flags |= SQFS_FLAG_ALWAYS_FRAGMENTS; - super->flags |= SQFS_FLAG_UNCOMPRESSED_FRAGMENTS; - - for (i = 0; i < tbl->table.used; ++i) { - sqfs_u32 sz = ((sqfs_fragment_t *)tbl->table.data)[i].size; - - if (SQFS_IS_BLOCK_COMPRESSED(le32toh(sz))) { - super->flags &= ~SQFS_FLAG_UNCOMPRESSED_FRAGMENTS; - break; - } - } - - return 0; -} - -int sqfs_frag_table_lookup(sqfs_frag_table_t *tbl, sqfs_u32 index, - sqfs_fragment_t *out) -{ - sqfs_fragment_t *frag = array_get(&tbl->table, index); - - if (frag == NULL) - return SQFS_ERROR_OUT_OF_BOUNDS; - - out->start_offset = le64toh(frag->start_offset); - out->size = le32toh(frag->size); - out->pad0 = le32toh(frag->pad0); - return 0; -} - -int sqfs_frag_table_append(sqfs_frag_table_t *tbl, sqfs_u64 location, - sqfs_u32 size, sqfs_u32 *index) -{ - sqfs_fragment_t frag; - - if (index != NULL) - *index = tbl->table.used; - - memset(&frag, 0, sizeof(frag)); - frag.start_offset = htole64(location); - frag.size = htole32(size); - - return array_append(&tbl->table, &frag); -} - -int sqfs_frag_table_set(sqfs_frag_table_t *tbl, sqfs_u32 index, - sqfs_u64 location, sqfs_u32 size) -{ - sqfs_fragment_t frag; - - memset(&frag, 0, sizeof(frag)); - frag.start_offset = htole64(location); - frag.size = htole32(size); - - return array_set(&tbl->table, index, &frag); -} - -size_t sqfs_frag_table_get_size(sqfs_frag_table_t *tbl) -{ - return tbl->table.used; -} diff --git a/lib/sqfs/id_table.c b/lib/sqfs/id_table.c deleted file mode 100644 index ec3fdfe..0000000 --- a/lib/sqfs/id_table.c +++ /dev/null @@ -1,162 +0,0 @@ -/* SPDX-License-Identifier: LGPL-3.0-or-later */ -/* - * id_table.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#define SQFS_BUILDING_DLL -#include "config.h" - -#include "sqfs/id_table.h" -#include "sqfs/super.h" -#include "sqfs/table.h" -#include "sqfs/error.h" -#include "compat.h" -#include "util/array.h" - -#include -#include - -struct sqfs_id_table_t { - sqfs_object_t base; - - array_t ids; -}; - -static void id_table_destroy(sqfs_object_t *obj) -{ - sqfs_id_table_t *tbl = (sqfs_id_table_t *)obj; - - array_cleanup(&tbl->ids); - free(tbl); -} - -static sqfs_object_t *id_table_copy(const sqfs_object_t *obj) -{ - const sqfs_id_table_t *tbl = (const sqfs_id_table_t *)obj; - sqfs_id_table_t *copy = calloc(1, sizeof(*copy)); - - if (copy == NULL) - return NULL; - - if (array_init_copy(©->ids, &tbl->ids) != 0) { - free(copy); - return NULL; - } - - return (sqfs_object_t *)copy; -} - -sqfs_id_table_t *sqfs_id_table_create(sqfs_u32 flags) -{ - sqfs_id_table_t *tbl; - - if (flags != 0) - return NULL; - - tbl = calloc(1, sizeof(sqfs_id_table_t)); - - if (tbl != NULL) { - array_init(&tbl->ids, sizeof(sqfs_u32), 0); - sqfs_object_init(tbl, id_table_destroy, id_table_copy); - } - - return tbl; -} - -int sqfs_id_table_id_to_index(sqfs_id_table_t *tbl, sqfs_u32 id, sqfs_u16 *out) -{ - size_t i; - - for (i = 0; i < tbl->ids.used; ++i) { - if (((sqfs_u32 *)tbl->ids.data)[i] == id) { - *out = i; - return 0; - } - } - - if (tbl->ids.used == 0x10000) - return SQFS_ERROR_OVERFLOW; - - *out = tbl->ids.used; - return array_append(&tbl->ids, &id); -} - -int sqfs_id_table_index_to_id(const sqfs_id_table_t *tbl, sqfs_u16 index, - sqfs_u32 *out) -{ - if (index >= tbl->ids.used) - return SQFS_ERROR_OUT_OF_BOUNDS; - - *out = ((sqfs_u32 *)tbl->ids.data)[index]; - return 0; -} - -int sqfs_id_table_read(sqfs_id_table_t *tbl, sqfs_file_t *file, - const sqfs_super_t *super, sqfs_compressor_t *cmp) -{ - sqfs_u64 upper_limit, lower_limit; - void *raw_ids; - size_t i; - int ret; - - if (!super->id_count || super->id_table_start >= super->bytes_used) - return SQFS_ERROR_CORRUPTED; - - upper_limit = super->id_table_start; - lower_limit = super->directory_table_start; - - if (super->fragment_table_start > lower_limit && - super->fragment_table_start < upper_limit) { - lower_limit = super->fragment_table_start; - } - - if (super->export_table_start > lower_limit && - super->export_table_start < upper_limit) { - lower_limit = super->export_table_start; - } - - array_cleanup(&tbl->ids); - tbl->ids.size = sizeof(sqfs_u32); - - ret = sqfs_read_table(file, cmp, super->id_count * sizeof(sqfs_u32), - super->id_table_start, lower_limit, - upper_limit, &raw_ids); - if (ret) - return ret; - - for (i = 0; i < super->id_count; ++i) - ((sqfs_u32 *)raw_ids)[i] = le32toh(((sqfs_u32 *)raw_ids)[i]); - - tbl->ids.data = raw_ids; - tbl->ids.used = super->id_count; - tbl->ids.count = super->id_count; - return 0; -} - -int sqfs_id_table_write(sqfs_id_table_t *tbl, sqfs_file_t *file, - sqfs_super_t *super, sqfs_compressor_t *cmp) -{ - sqfs_u64 start; - size_t i; - int ret; - - for (i = 0; i < tbl->ids.used; ++i) { - ((sqfs_u32 *)tbl->ids.data)[i] = - htole32(((sqfs_u32 *)tbl->ids.data)[i]); - } - - super->id_count = tbl->ids.used; - - ret = sqfs_write_table(file, cmp, tbl->ids.data, - sizeof(sqfs_u32) * tbl->ids.used, &start); - - super->id_table_start = start; - - for (i = 0; i < tbl->ids.used; ++i) { - ((sqfs_u32 *)tbl->ids.data)[i] = - le32toh(((sqfs_u32 *)tbl->ids.data)[i]); - } - - return ret; -} diff --git a/lib/sqfs/inode.c b/lib/sqfs/inode.c deleted file mode 100644 index ce51cf5..0000000 --- a/lib/sqfs/inode.c +++ /dev/null @@ -1,380 +0,0 @@ -/* SPDX-License-Identifier: LGPL-3.0-or-later */ -/* - * inode.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#define SQFS_BUILDING_DLL -#include "config.h" - -#include "sqfs/inode.h" -#include "sqfs/error.h" -#include "sqfs/dir.h" - -#include -#include - -#include "util/util.h" - -static int inverse_type[] = { - [SQFS_INODE_DIR] = SQFS_INODE_EXT_DIR, - [SQFS_INODE_FILE] = SQFS_INODE_EXT_FILE, - [SQFS_INODE_SLINK] = SQFS_INODE_EXT_SLINK, - [SQFS_INODE_BDEV] = SQFS_INODE_EXT_BDEV, - [SQFS_INODE_CDEV] = SQFS_INODE_EXT_CDEV, - [SQFS_INODE_FIFO] = SQFS_INODE_EXT_FIFO, - [SQFS_INODE_SOCKET] = SQFS_INODE_EXT_SOCKET, - [SQFS_INODE_EXT_DIR] = SQFS_INODE_DIR, - [SQFS_INODE_EXT_FILE] = SQFS_INODE_FILE, - [SQFS_INODE_EXT_SLINK] = SQFS_INODE_SLINK, - [SQFS_INODE_EXT_BDEV] = SQFS_INODE_BDEV, - [SQFS_INODE_EXT_CDEV] = SQFS_INODE_CDEV, - [SQFS_INODE_EXT_FIFO] = SQFS_INODE_FIFO, - [SQFS_INODE_EXT_SOCKET] = SQFS_INODE_SOCKET, -}; - -int sqfs_inode_get_xattr_index(const sqfs_inode_generic_t *inode, - sqfs_u32 *out) -{ - switch (inode->base.type) { - case SQFS_INODE_DIR: - case SQFS_INODE_FILE: - case SQFS_INODE_SLINK: - case SQFS_INODE_BDEV: - case SQFS_INODE_CDEV: - case SQFS_INODE_FIFO: - case SQFS_INODE_SOCKET: - *out = 0xFFFFFFFF; - break; - case SQFS_INODE_EXT_DIR: - *out = inode->data.dir_ext.xattr_idx; - break; - case SQFS_INODE_EXT_FILE: - *out = inode->data.file_ext.xattr_idx; - break; - case SQFS_INODE_EXT_SLINK: - *out = inode->data.slink_ext.xattr_idx; - break; - case SQFS_INODE_EXT_BDEV: - case SQFS_INODE_EXT_CDEV: - *out = inode->data.dev_ext.xattr_idx; - break; - case SQFS_INODE_EXT_FIFO: - case SQFS_INODE_EXT_SOCKET: - *out = inode->data.ipc_ext.xattr_idx; - break; - default: - return SQFS_ERROR_CORRUPTED; - } - - return 0; -} - -int sqfs_inode_set_xattr_index(sqfs_inode_generic_t *inode, sqfs_u32 index) -{ - int err; - - if (index != 0xFFFFFFFF) { - err = sqfs_inode_make_extended(inode); - if (err) - return err; - } - - switch (inode->base.type) { - case SQFS_INODE_DIR: - case SQFS_INODE_FILE: - case SQFS_INODE_SLINK: - case SQFS_INODE_BDEV: - case SQFS_INODE_CDEV: - case SQFS_INODE_FIFO: - case SQFS_INODE_SOCKET: - break; - case SQFS_INODE_EXT_DIR: - inode->data.dir_ext.xattr_idx = index; - break; - case SQFS_INODE_EXT_FILE: - inode->data.file_ext.xattr_idx = index; - break; - case SQFS_INODE_EXT_SLINK: - inode->data.slink_ext.xattr_idx = index; - break; - case SQFS_INODE_EXT_BDEV: - case SQFS_INODE_EXT_CDEV: - inode->data.dev_ext.xattr_idx = index; - break; - case SQFS_INODE_EXT_FIFO: - case SQFS_INODE_EXT_SOCKET: - inode->data.ipc_ext.xattr_idx = index; - break; - default: - return SQFS_ERROR_CORRUPTED; - } - - return 0; -} - -int sqfs_inode_make_extended(sqfs_inode_generic_t *inode) -{ - switch (inode->base.type) { - case SQFS_INODE_DIR: { - sqfs_inode_dir_ext_t temp = { - .nlink = inode->data.dir.nlink, - .size = inode->data.dir.size, - .start_block = inode->data.dir.start_block, - .parent_inode = inode->data.dir.parent_inode, - .inodex_count = 0, - .offset = inode->data.dir.offset, - .xattr_idx = 0xFFFFFFFF, - }; - inode->data.dir_ext = temp; - break; - } - case SQFS_INODE_FILE: { - sqfs_inode_file_ext_t temp = { - .blocks_start = inode->data.file.blocks_start, - .file_size = inode->data.file.file_size, - .sparse = 0, - .nlink = 1, - .fragment_idx = inode->data.file.fragment_index, - .fragment_offset = inode->data.file.fragment_offset, - .xattr_idx = 0xFFFFFFFF, - }; - inode->data.file_ext = temp; - break; - } - case SQFS_INODE_SLINK: - inode->data.slink_ext.xattr_idx = 0xFFFFFFFF; - break; - case SQFS_INODE_BDEV: - case SQFS_INODE_CDEV: - inode->data.dev_ext.xattr_idx = 0xFFFFFFFF; - break; - case SQFS_INODE_FIFO: - case SQFS_INODE_SOCKET: - inode->data.dev_ext.xattr_idx = 0xFFFFFFFF; - break; - case SQFS_INODE_EXT_DIR: - case SQFS_INODE_EXT_FILE: - case SQFS_INODE_EXT_SLINK: - case SQFS_INODE_EXT_BDEV: - case SQFS_INODE_EXT_CDEV: - case SQFS_INODE_EXT_FIFO: - case SQFS_INODE_EXT_SOCKET: - return 0; - default: - return SQFS_ERROR_CORRUPTED; - } - - inode->base.type = inverse_type[inode->base.type]; - return 0; -} - -int sqfs_inode_make_basic(sqfs_inode_generic_t *inode) -{ - sqfs_u32 xattr; - int err; - - err = sqfs_inode_get_xattr_index(inode, &xattr); - if (err != 0 || xattr != 0xFFFFFFFF) - return err; - - switch (inode->base.type) { - case SQFS_INODE_DIR: - case SQFS_INODE_FILE: - case SQFS_INODE_SLINK: - case SQFS_INODE_BDEV: - case SQFS_INODE_CDEV: - case SQFS_INODE_FIFO: - case SQFS_INODE_SOCKET: - return 0; - case SQFS_INODE_EXT_DIR: { - sqfs_inode_dir_t temp = { - .start_block = inode->data.dir_ext.start_block, - .nlink = inode->data.dir_ext.nlink, - .size = inode->data.dir_ext.size, - .offset = inode->data.dir_ext.offset, - .parent_inode = inode->data.dir_ext.parent_inode, - }; - - if (inode->data.dir_ext.size > 0x0FFFF) - return 0; - - inode->data.dir = temp; - break; - } - case SQFS_INODE_EXT_FILE: { - sqfs_inode_file_t temp = { - .blocks_start = inode->data.file_ext.blocks_start, - .fragment_index = inode->data.file_ext.fragment_idx, - .fragment_offset = inode->data.file_ext.fragment_offset, - .file_size = inode->data.file_ext.file_size, - }; - - if (inode->data.file_ext.blocks_start > 0x0FFFFFFFFUL) - return 0; - if (inode->data.file_ext.file_size > 0x0FFFFFFFFUL) - return 0; - if (inode->data.file_ext.sparse > 0) - return 0; - if (inode->data.file_ext.nlink > 1) - return 0; - - inode->data.file = temp; - break; - } - case SQFS_INODE_EXT_SLINK: - case SQFS_INODE_EXT_BDEV: - case SQFS_INODE_EXT_CDEV: - case SQFS_INODE_EXT_FIFO: - case SQFS_INODE_EXT_SOCKET: - break; - default: - return SQFS_ERROR_CORRUPTED; - } - - inode->base.type = inverse_type[inode->base.type]; - return 0; -} - -int sqfs_inode_set_file_size(sqfs_inode_generic_t *inode, sqfs_u64 size) -{ - if (inode->base.type == SQFS_INODE_EXT_FILE) { - inode->data.file_ext.file_size = size; - - if (size < 0x0FFFFFFFFUL) - sqfs_inode_make_basic(inode); - } else if (inode->base.type == SQFS_INODE_FILE) { - if (size > 0x0FFFFFFFFUL) { - sqfs_inode_make_extended(inode); - inode->data.file_ext.file_size = size; - } else { - inode->data.file.file_size = size; - } - } else { - return SQFS_ERROR_NOT_FILE; - } - - return 0; -} - -int sqfs_inode_set_frag_location(sqfs_inode_generic_t *inode, - sqfs_u32 index, sqfs_u32 offset) -{ - if (inode->base.type == SQFS_INODE_EXT_FILE) { - inode->data.file_ext.fragment_idx = index; - inode->data.file_ext.fragment_offset = offset; - } else if (inode->base.type == SQFS_INODE_FILE) { - inode->data.file.fragment_index = index; - inode->data.file.fragment_offset = offset; - } else { - return SQFS_ERROR_NOT_FILE; - } - - return 0; -} - -int sqfs_inode_set_file_block_start(sqfs_inode_generic_t *inode, - sqfs_u64 location) -{ - if (inode->base.type == SQFS_INODE_EXT_FILE) { - inode->data.file_ext.blocks_start = location; - - if (location < 0x0FFFFFFFFUL) - sqfs_inode_make_basic(inode); - } else if (inode->base.type == SQFS_INODE_FILE) { - if (location > 0x0FFFFFFFFUL) { - sqfs_inode_make_extended(inode); - inode->data.file_ext.blocks_start = location; - } else { - inode->data.file.blocks_start = location; - } - } else { - return SQFS_ERROR_NOT_FILE; - } - - return 0; -} - -int sqfs_inode_get_file_size(const sqfs_inode_generic_t *inode, sqfs_u64 *size) -{ - if (inode->base.type == SQFS_INODE_EXT_FILE) { - *size = inode->data.file_ext.file_size; - } else if (inode->base.type == SQFS_INODE_FILE) { - *size = inode->data.file.file_size; - } else { - return SQFS_ERROR_NOT_FILE; - } - - return 0; -} - -int sqfs_inode_get_frag_location(const sqfs_inode_generic_t *inode, - sqfs_u32 *index, sqfs_u32 *offset) -{ - if (inode->base.type == SQFS_INODE_EXT_FILE) { - *index = inode->data.file_ext.fragment_idx; - *offset = inode->data.file_ext.fragment_offset; - } else if (inode->base.type == SQFS_INODE_FILE) { - *index = inode->data.file.fragment_index; - *offset = inode->data.file.fragment_offset; - } else { - return SQFS_ERROR_NOT_FILE; - } - - return 0; -} - -int sqfs_inode_get_file_block_start(const sqfs_inode_generic_t *inode, - sqfs_u64 *location) -{ - if (inode->base.type == SQFS_INODE_EXT_FILE) { - *location = inode->data.file_ext.blocks_start; - } else if (inode->base.type == SQFS_INODE_FILE) { - *location = inode->data.file.blocks_start; - } else { - return SQFS_ERROR_NOT_FILE; - } - - return 0; -} - -int sqfs_inode_unpack_dir_index_entry(const sqfs_inode_generic_t *inode, - sqfs_dir_index_t **out, - size_t index) -{ - sqfs_dir_index_t ent; - const char *ptr; - size_t offset; - - if (inode->base.type != SQFS_INODE_EXT_DIR) { - if (inode->base.type == SQFS_INODE_DIR) - return SQFS_ERROR_OUT_OF_BOUNDS; - - return SQFS_ERROR_NOT_DIR; - } - - offset = 0; - ptr = (const char *)inode->extra; - - for (;;) { - if (offset >= inode->payload_bytes_used) - return SQFS_ERROR_OUT_OF_BOUNDS; - - if (index == 0) - break; - - memcpy(&ent, ptr + offset, sizeof(ent)); - offset += sizeof(ent) + ent.size + 1; - index -= 1; - } - - memcpy(&ent, ptr + offset, sizeof(ent)); - - *out = alloc_flex(sizeof(ent), 1, ent.size + 2); - if (*out == NULL) - return SQFS_ERROR_ALLOC; - - memcpy(*out, &ent, sizeof(ent)); - memcpy((*out)->name, ptr + offset + sizeof(ent), ent.size + 1); - return 0; -} diff --git a/lib/sqfs/meta_reader.c b/lib/sqfs/meta_reader.c deleted file mode 100644 index e431d40..0000000 --- a/lib/sqfs/meta_reader.c +++ /dev/null @@ -1,191 +0,0 @@ -/* SPDX-License-Identifier: LGPL-3.0-or-later */ -/* - * meta_reader.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#define SQFS_BUILDING_DLL -#include "config.h" - -#include "sqfs/meta_reader.h" -#include "sqfs/compressor.h" -#include "sqfs/error.h" -#include "sqfs/block.h" -#include "sqfs/io.h" -#include "util/util.h" - -#include -#include - -struct sqfs_meta_reader_t { - sqfs_object_t base; - - sqfs_u64 start; - sqfs_u64 limit; - size_t data_used; - - /* The location of the current block in the image */ - sqfs_u64 block_offset; - - /* The location of the next block after the current one */ - sqfs_u64 next_block; - - /* A byte offset into the uncompressed data of the current block */ - size_t offset; - - /* The underlying file descriptor to read from */ - sqfs_file_t *file; - - /* A pointer to the compressor to use for extracting data */ - sqfs_compressor_t *cmp; - - /* The raw data read from the input file */ - sqfs_u8 data[SQFS_META_BLOCK_SIZE]; - - /* The uncompressed data read from the input file */ - sqfs_u8 scratch[SQFS_META_BLOCK_SIZE]; -}; - -static void meta_reader_destroy(sqfs_object_t *m) -{ - sqfs_meta_reader_t *mr = (sqfs_meta_reader_t *)m; - - sqfs_drop(mr->file); - sqfs_drop(mr->cmp); - free(m); -} - -static sqfs_object_t *meta_reader_copy(const sqfs_object_t *obj) -{ - const sqfs_meta_reader_t *m = (const sqfs_meta_reader_t *)obj; - sqfs_meta_reader_t *copy = malloc(sizeof(*copy)); - - if (copy != NULL) { - memcpy(copy, m, sizeof(*m)); - - /* duplicate references */ - copy->cmp = sqfs_grab(copy->cmp); - copy->file = sqfs_grab(copy->file); - } - - return (sqfs_object_t *)copy; -} - -sqfs_meta_reader_t *sqfs_meta_reader_create(sqfs_file_t *file, - sqfs_compressor_t *cmp, - sqfs_u64 start, sqfs_u64 limit) -{ - sqfs_meta_reader_t *m = calloc(1, sizeof(*m)); - - if (m == NULL) - return NULL; - - sqfs_object_init(m, meta_reader_destroy, meta_reader_copy); - - m->block_offset = 0xFFFFFFFFFFFFFFFFUL; - m->start = start; - m->limit = limit; - m->file = sqfs_grab(file); - m->cmp = sqfs_grab(cmp); - return m; -} - -int sqfs_meta_reader_seek(sqfs_meta_reader_t *m, sqfs_u64 block_start, - size_t offset) -{ - bool compressed; - sqfs_u16 header; - sqfs_u32 size; - sqfs_s32 ret; - int err; - - if (block_start < m->start || block_start >= m->limit) - return SQFS_ERROR_OUT_OF_BOUNDS; - - if (block_start == m->block_offset) { - if (offset >= m->data_used) - return SQFS_ERROR_OUT_OF_BOUNDS; - - m->offset = offset; - return 0; - } - - err = m->file->read_at(m->file, block_start, &header, 2); - if (err) - return err; - - header = le16toh(header); - compressed = (header & 0x8000) == 0; - size = header & 0x7FFF; - - if (size > sizeof(m->data)) - return SQFS_ERROR_CORRUPTED; - - if ((block_start + 2 + size) > m->limit) - return SQFS_ERROR_OUT_OF_BOUNDS; - - err = m->file->read_at(m->file, block_start + 2, m->data, size); - if (err) - return err; - - if (compressed) { - ret = m->cmp->do_block(m->cmp, m->data, size, - m->scratch, sizeof(m->scratch)); - - if (ret < 0) - return ret; - - memcpy(m->data, m->scratch, ret); - m->data_used = ret; - } else { - m->data_used = size; - } - - if (offset >= m->data_used) - return SQFS_ERROR_OUT_OF_BOUNDS; - - m->block_offset = block_start; - m->next_block = block_start + size + 2; - m->offset = offset; - return 0; -} - -void sqfs_meta_reader_get_position(const sqfs_meta_reader_t *m, - sqfs_u64 *block_start, size_t *offset) -{ - if (m->offset == m->data_used) { - *block_start = m->next_block; - *offset = 0; - } else { - *block_start = m->block_offset; - *offset = m->offset; - } -} - -int sqfs_meta_reader_read(sqfs_meta_reader_t *m, void *data, size_t size) -{ - size_t diff; - int ret; - - while (size != 0) { - diff = m->data_used - m->offset; - - if (diff == 0) { - ret = sqfs_meta_reader_seek(m, m->next_block, 0); - if (ret) - return ret; - diff = m->data_used; - } - - if (diff > size) - diff = size; - - memcpy(data, m->data + m->offset, diff); - - m->offset += diff; - data = (char *)data + diff; - size -= diff; - } - - return 0; -} diff --git a/lib/sqfs/meta_writer.c b/lib/sqfs/meta_writer.c deleted file mode 100644 index bf3f426..0000000 --- a/lib/sqfs/meta_writer.c +++ /dev/null @@ -1,215 +0,0 @@ -/* SPDX-License-Identifier: LGPL-3.0-or-later */ -/* - * meta_writer.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#define SQFS_BUILDING_DLL -#include "config.h" - -#include "sqfs/meta_writer.h" -#include "sqfs/compressor.h" -#include "sqfs/error.h" -#include "sqfs/block.h" -#include "sqfs/io.h" -#include "util/util.h" - -#include -#include - -typedef struct meta_block_t { - struct meta_block_t *next; - - /* possibly compressed data with 2 byte header */ - sqfs_u8 data[SQFS_META_BLOCK_SIZE + 2]; -} meta_block_t; - -struct sqfs_meta_writer_t { - sqfs_object_t base; - - /* A byte offset into the uncompressed data of the current block */ - size_t offset; - - /* The location of the current block in the file */ - size_t block_offset; - - /* The underlying file descriptor to write to */ - sqfs_file_t *file; - - /* A pointer to the compressor to use for compressing the data */ - sqfs_compressor_t *cmp; - - /* The raw data chunk that data is appended to */ - sqfs_u8 data[SQFS_META_BLOCK_SIZE]; - - sqfs_u32 flags; - meta_block_t *list; - meta_block_t *list_end; -}; - -static int write_block(sqfs_file_t *file, meta_block_t *outblk) -{ - sqfs_u16 header; - size_t count; - sqfs_u64 off; - - memcpy(&header, outblk->data, sizeof(header)); - count = le16toh(header) & 0x7FFF; - off = file->get_size(file); - - return file->write_at(file, off, outblk->data, count + 2); -} - -static void meta_writer_destroy(sqfs_object_t *obj) -{ - sqfs_meta_writer_t *m = (sqfs_meta_writer_t *)obj; - meta_block_t *blk; - - while (m->list != NULL) { - blk = m->list; - m->list = blk->next; - free(blk); - } - - sqfs_drop(m->file); - sqfs_drop(m->cmp); - free(m); -} - -sqfs_meta_writer_t *sqfs_meta_writer_create(sqfs_file_t *file, - sqfs_compressor_t *cmp, - sqfs_u32 flags) -{ - sqfs_meta_writer_t *m; - - if (flags & ~SQFS_META_WRITER_ALL_FLAGS) - return NULL; - - m = calloc(1, sizeof(*m)); - if (m == NULL) - return NULL; - - sqfs_object_init(m, meta_writer_destroy, NULL); - - m->cmp = sqfs_grab(cmp); - m->file = sqfs_grab(file); - m->flags = flags; - return m; -} - -int sqfs_meta_writer_flush(sqfs_meta_writer_t *m) -{ - meta_block_t *outblk; - sqfs_u16 header; - sqfs_u32 count; - sqfs_s32 ret; - - if (m->offset == 0) - return 0; - - outblk = calloc(1, sizeof(*outblk)); - if (outblk == NULL) - return SQFS_ERROR_ALLOC; - - ret = m->cmp->do_block(m->cmp, m->data, m->offset, - outblk->data + 2, sizeof(outblk->data) - 2); - if (ret < 0) { - free(outblk); - return ret; - } - - if (ret > 0) { - header = htole16(ret); - count = ret + 2; - } else { - header = htole16(m->offset | 0x8000); - memcpy(outblk->data + 2, m->data, m->offset); - count = m->offset + 2; - } - - memcpy(outblk->data, &header, sizeof(header)); - - ret = 0; - - if (m->flags & SQFS_META_WRITER_KEEP_IN_MEMORY) { - if (m->list == NULL) { - m->list = outblk; - } else { - m->list_end->next = outblk; - } - m->list_end = outblk; - } else { - ret = write_block(m->file, outblk); - free(outblk); - } - - memset(m->data, 0, sizeof(m->data)); - m->offset = 0; - m->block_offset += count; - return ret; -} - -int sqfs_meta_writer_append(sqfs_meta_writer_t *m, const void *data, - size_t size) -{ - size_t diff; - int ret; - - while (size != 0) { - diff = sizeof(m->data) - m->offset; - - if (diff == 0) { - ret = sqfs_meta_writer_flush(m); - if (ret) - return ret; - diff = sizeof(m->data); - } - - if (diff > size) - diff = size; - - memcpy(m->data + m->offset, data, diff); - m->offset += diff; - size -= diff; - data = (const char *)data + diff; - } - - if (m->offset == sizeof(m->data)) - return sqfs_meta_writer_flush(m); - - return 0; -} - -void sqfs_meta_writer_get_position(const sqfs_meta_writer_t *m, - sqfs_u64 *block_start, - sqfs_u32 *offset) -{ - *block_start = m->block_offset; - *offset = m->offset; -} - -void sqfs_meta_writer_reset(sqfs_meta_writer_t *m) -{ - m->block_offset = 0; - m->offset = 0; -} - -int sqfs_meta_write_write_to_file(sqfs_meta_writer_t *m) -{ - meta_block_t *blk; - int ret; - - while (m->list != NULL) { - blk = m->list; - - ret = write_block(m->file, blk); - if (ret) - return ret; - - m->list = blk->next; - free(blk); - } - - m->list_end = NULL; - return 0; -} diff --git a/lib/sqfs/misc.c b/lib/sqfs/misc.c deleted file mode 100644 index 74a4203..0000000 --- a/lib/sqfs/misc.c +++ /dev/null @@ -1,17 +0,0 @@ -/* SPDX-License-Identifier: LGPL-3.0-or-later */ -/* - * misc.c - * - * Copyright (C) 2021 David Oberhollenzer - */ -#define SQFS_BUILDING_DLL -#include "config.h" - -#include "sqfs/predef.h" - -#include - -void sqfs_free(void *ptr) -{ - free(ptr); -} diff --git a/lib/sqfs/read_inode.c b/lib/sqfs/read_inode.c deleted file mode 100644 index 12bef48..0000000 --- a/lib/sqfs/read_inode.c +++ /dev/null @@ -1,424 +0,0 @@ -/* SPDX-License-Identifier: LGPL-3.0-or-later */ -/* - * read_inode.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#define SQFS_BUILDING_DLL -#include "config.h" - -#include "sqfs/meta_reader.h" -#include "sqfs/error.h" -#include "sqfs/super.h" -#include "sqfs/inode.h" -#include "sqfs/dir.h" -#include "util/util.h" - -#include -#include -#include - -#define SWAB16(x) x = le16toh(x) -#define SWAB32(x) x = le32toh(x) -#define SWAB64(x) x = le64toh(x) - -static int set_mode(sqfs_inode_t *inode) -{ - inode->mode &= ~S_IFMT; - - switch (inode->type) { - case SQFS_INODE_SOCKET: - case SQFS_INODE_EXT_SOCKET: - inode->mode |= S_IFSOCK; - break; - case SQFS_INODE_SLINK: - case SQFS_INODE_EXT_SLINK: - inode->mode |= S_IFLNK; - break; - case SQFS_INODE_FILE: - case SQFS_INODE_EXT_FILE: - inode->mode |= S_IFREG; - break; - case SQFS_INODE_BDEV: - case SQFS_INODE_EXT_BDEV: - inode->mode |= S_IFBLK; - break; - case SQFS_INODE_DIR: - case SQFS_INODE_EXT_DIR: - inode->mode |= S_IFDIR; - break; - case SQFS_INODE_CDEV: - case SQFS_INODE_EXT_CDEV: - inode->mode |= S_IFCHR; - break; - case SQFS_INODE_FIFO: - case SQFS_INODE_EXT_FIFO: - inode->mode |= S_IFIFO; - break; - default: - return SQFS_ERROR_UNSUPPORTED; - } - - return 0; -} - -static sqfs_u64 get_block_count(sqfs_u64 size, sqfs_u64 block_size, - sqfs_u32 frag_index, sqfs_u32 frag_offset) -{ - sqfs_u64 count = size / block_size; - - if ((size % block_size) != 0 && - (frag_index == 0xFFFFFFFF || frag_offset == 0xFFFFFFFF)) { - ++count; - } - - return count; -} - -static int read_inode_file(sqfs_meta_reader_t *ir, sqfs_inode_t *base, - size_t block_size, sqfs_inode_generic_t **result) -{ - sqfs_inode_generic_t *out; - sqfs_inode_file_t file; - sqfs_u64 i, count; - int err; - - err = sqfs_meta_reader_read(ir, &file, sizeof(file)); - if (err) - return err; - - SWAB32(file.blocks_start); - SWAB32(file.fragment_index); - SWAB32(file.fragment_offset); - SWAB32(file.file_size); - - count = get_block_count(file.file_size, block_size, - file.fragment_index, file.fragment_offset); - - out = alloc_flex(sizeof(*out), sizeof(sqfs_u32), count); - if (out == NULL) - return SQFS_ERROR_ALLOC; - - out->base = *base; - out->data.file = file; - out->payload_bytes_available = count * sizeof(sqfs_u32); - out->payload_bytes_used = count * sizeof(sqfs_u32); - - err = sqfs_meta_reader_read(ir, out->extra, count * sizeof(sqfs_u32)); - if (err) { - free(out); - return err; - } - - for (i = 0; i < count; ++i) - SWAB32(out->extra[i]); - - *result = out; - return 0; -} - -static int read_inode_file_ext(sqfs_meta_reader_t *ir, sqfs_inode_t *base, - size_t block_size, sqfs_inode_generic_t **result) -{ - sqfs_inode_file_ext_t file; - sqfs_inode_generic_t *out; - sqfs_u64 i, count; - int err; - - err = sqfs_meta_reader_read(ir, &file, sizeof(file)); - if (err) - return err; - - SWAB64(file.blocks_start); - SWAB64(file.file_size); - SWAB64(file.sparse); - SWAB32(file.nlink); - SWAB32(file.fragment_idx); - SWAB32(file.fragment_offset); - SWAB32(file.xattr_idx); - - count = get_block_count(file.file_size, block_size, - file.fragment_idx, file.fragment_offset); - - out = alloc_flex(sizeof(*out), sizeof(sqfs_u32), count); - if (out == NULL) { - return errno == EOVERFLOW ? SQFS_ERROR_OVERFLOW : - SQFS_ERROR_ALLOC; - } - - out->base = *base; - out->data.file_ext = file; - out->payload_bytes_available = count * sizeof(sqfs_u32); - out->payload_bytes_used = count * sizeof(sqfs_u32); - - err = sqfs_meta_reader_read(ir, out->extra, count * sizeof(sqfs_u32)); - if (err) { - free(out); - return err; - } - - for (i = 0; i < count; ++i) - SWAB32(out->extra[i]); - - *result = out; - return 0; -} - -static int read_inode_slink(sqfs_meta_reader_t *ir, sqfs_inode_t *base, - sqfs_inode_generic_t **result) -{ - sqfs_inode_generic_t *out; - sqfs_inode_slink_t slink; - size_t size; - int err; - - err = sqfs_meta_reader_read(ir, &slink, sizeof(slink)); - if (err) - return err; - - SWAB32(slink.nlink); - SWAB32(slink.target_size); - - if (SZ_ADD_OV(slink.target_size, 1, &size) || - SZ_ADD_OV(sizeof(*out), size, &size)) { - return SQFS_ERROR_OVERFLOW; - } - - out = calloc(1, size); - if (out == NULL) - return SQFS_ERROR_ALLOC; - - out->payload_bytes_available = size - sizeof(*out); - out->payload_bytes_used = size - sizeof(*out) - 1; - out->base = *base; - out->data.slink = slink; - - err = sqfs_meta_reader_read(ir, (void *)out->extra, slink.target_size); - if (err) { - free(out); - return err; - } - - *result = out; - return 0; -} - -static int read_inode_slink_ext(sqfs_meta_reader_t *ir, sqfs_inode_t *base, - sqfs_inode_generic_t **result) -{ - sqfs_u32 xattr; - int err; - - err = read_inode_slink(ir, base, result); - if (err) - return err; - - err = sqfs_meta_reader_read(ir, &xattr, sizeof(xattr)); - if (err) { - free(*result); - return err; - } - - (*result)->data.slink_ext.xattr_idx = le32toh(xattr); - return 0; -} - -static int read_inode_dir_ext(sqfs_meta_reader_t *ir, sqfs_inode_t *base, - sqfs_inode_generic_t **result) -{ - size_t i, new_sz, index_max, index_used; - sqfs_inode_generic_t *out, *new; - sqfs_inode_dir_ext_t dir; - sqfs_dir_index_t ent; - int err; - - err = sqfs_meta_reader_read(ir, &dir, sizeof(dir)); - if (err) - return err; - - SWAB32(dir.nlink); - SWAB32(dir.size); - SWAB32(dir.start_block); - SWAB32(dir.parent_inode); - SWAB16(dir.inodex_count); - SWAB16(dir.offset); - SWAB32(dir.xattr_idx); - - index_max = dir.size ? 128 : 0; - index_used = 0; - - out = alloc_flex(sizeof(*out), 1, index_max); - if (out == NULL) - return SQFS_ERROR_ALLOC; - - out->base = *base; - out->data.dir_ext = dir; - - if (dir.size == 0) { - *result = out; - return 0; - } - - for (i = 0; i < dir.inodex_count; ++i) { - err = sqfs_meta_reader_read(ir, &ent, sizeof(ent)); - if (err) { - free(out); - return err; - } - - SWAB32(ent.start_block); - SWAB32(ent.index); - SWAB32(ent.size); - - new_sz = index_max; - while (sizeof(ent) + ent.size + 1 > new_sz - index_used) { - if (SZ_MUL_OV(new_sz, 2, &new_sz)) { - free(out); - return SQFS_ERROR_OVERFLOW; - } - } - - if (new_sz > index_max) { - new = realloc(out, sizeof(*out) + new_sz); - if (new == NULL) { - free(out); - return SQFS_ERROR_ALLOC; - } - out = new; - index_max = new_sz; - } - - memcpy((char *)out->extra + index_used, &ent, sizeof(ent)); - index_used += sizeof(ent); - - err = sqfs_meta_reader_read(ir, (char *)out->extra + index_used, - ent.size + 1); - if (err) { - free(out); - return err; - } - - index_used += ent.size + 1; - } - - out->payload_bytes_used = index_used; - out->payload_bytes_available = index_used; - *result = out; - return 0; -} - -int sqfs_meta_reader_read_inode(sqfs_meta_reader_t *ir, - const sqfs_super_t *super, - sqfs_u64 block_start, size_t offset, - sqfs_inode_generic_t **result) -{ - sqfs_inode_generic_t *out; - sqfs_inode_t inode; - int err; - - /* read base inode */ - block_start += super->inode_table_start; - - err = sqfs_meta_reader_seek(ir, block_start, offset); - if (err) - return err; - - err = sqfs_meta_reader_read(ir, &inode, sizeof(inode)); - if (err) - return err; - - SWAB16(inode.type); - SWAB16(inode.mode); - SWAB16(inode.uid_idx); - SWAB16(inode.gid_idx); - SWAB32(inode.mod_time); - SWAB32(inode.inode_number); - - err = set_mode(&inode); - if (err) - return err; - - /* inode types where the size is variable */ - switch (inode.type) { - case SQFS_INODE_FILE: - return read_inode_file(ir, &inode, super->block_size, result); - case SQFS_INODE_SLINK: - return read_inode_slink(ir, &inode, result); - case SQFS_INODE_EXT_FILE: - return read_inode_file_ext(ir, &inode, super->block_size, - result); - case SQFS_INODE_EXT_SLINK: - return read_inode_slink_ext(ir, &inode, result); - case SQFS_INODE_EXT_DIR: - return read_inode_dir_ext(ir, &inode, result); - default: - break; - } - - /* everything else */ - out = calloc(1, sizeof(*out)); - if (out == NULL) - return SQFS_ERROR_ALLOC; - - out->base = inode; - - switch (inode.type) { - case SQFS_INODE_DIR: - err = sqfs_meta_reader_read(ir, &out->data.dir, - sizeof(out->data.dir)); - if (err) - goto fail_free; - - SWAB32(out->data.dir.start_block); - SWAB32(out->data.dir.nlink); - SWAB16(out->data.dir.size); - SWAB16(out->data.dir.offset); - SWAB32(out->data.dir.parent_inode); - break; - case SQFS_INODE_BDEV: - case SQFS_INODE_CDEV: - err = sqfs_meta_reader_read(ir, &out->data.dev, - sizeof(out->data.dev)); - if (err) - goto fail_free; - SWAB32(out->data.dev.nlink); - SWAB32(out->data.dev.devno); - break; - case SQFS_INODE_FIFO: - case SQFS_INODE_SOCKET: - err = sqfs_meta_reader_read(ir, &out->data.ipc, - sizeof(out->data.ipc)); - if (err) - goto fail_free; - SWAB32(out->data.ipc.nlink); - break; - case SQFS_INODE_EXT_BDEV: - case SQFS_INODE_EXT_CDEV: - err = sqfs_meta_reader_read(ir, &out->data.dev_ext, - sizeof(out->data.dev_ext)); - if (err) - goto fail_free; - SWAB32(out->data.dev_ext.nlink); - SWAB32(out->data.dev_ext.devno); - SWAB32(out->data.dev_ext.xattr_idx); - break; - case SQFS_INODE_EXT_FIFO: - case SQFS_INODE_EXT_SOCKET: - err = sqfs_meta_reader_read(ir, &out->data.ipc_ext, - sizeof(out->data.ipc_ext)); - if (err) - goto fail_free; - SWAB32(out->data.ipc_ext.nlink); - SWAB32(out->data.ipc_ext.xattr_idx); - break; - default: - err = SQFS_ERROR_UNSUPPORTED; - goto fail_free; - } - - *result = out; - return 0; -fail_free: - free(out); - return err; -} diff --git a/lib/sqfs/read_super.c b/lib/sqfs/read_super.c deleted file mode 100644 index 11bc314..0000000 --- a/lib/sqfs/read_super.c +++ /dev/null @@ -1,83 +0,0 @@ -/* SPDX-License-Identifier: LGPL-3.0-or-later */ -/* - * read_super.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#define SQFS_BUILDING_DLL -#include "config.h" - -#include "sqfs/super.h" -#include "sqfs/error.h" -#include "sqfs/io.h" -#include "util/util.h" - -#include - -int sqfs_super_read(sqfs_super_t *super, sqfs_file_t *file) -{ - size_t block_size = 0; - sqfs_super_t temp; - int i, ret; - - ret = file->read_at(file, 0, &temp, sizeof(temp)); - if (ret) - return ret; - - temp.magic = le32toh(temp.magic); - temp.inode_count = le32toh(temp.inode_count); - temp.modification_time = le32toh(temp.modification_time); - temp.block_size = le32toh(temp.block_size); - temp.fragment_entry_count = le32toh(temp.fragment_entry_count); - temp.compression_id = le16toh(temp.compression_id); - temp.block_log = le16toh(temp.block_log); - temp.flags = le16toh(temp.flags); - temp.id_count = le16toh(temp.id_count); - temp.version_major = le16toh(temp.version_major); - temp.version_minor = le16toh(temp.version_minor); - temp.root_inode_ref = le64toh(temp.root_inode_ref); - temp.bytes_used = le64toh(temp.bytes_used); - temp.id_table_start = le64toh(temp.id_table_start); - temp.xattr_id_table_start = le64toh(temp.xattr_id_table_start); - temp.inode_table_start = le64toh(temp.inode_table_start); - temp.directory_table_start = le64toh(temp.directory_table_start); - temp.fragment_table_start = le64toh(temp.fragment_table_start); - temp.export_table_start = le64toh(temp.export_table_start); - - if (temp.magic != SQFS_MAGIC) - return SFQS_ERROR_SUPER_MAGIC; - - if ((temp.version_major != SQFS_VERSION_MAJOR) || - (temp.version_minor != SQFS_VERSION_MINOR)) - return SFQS_ERROR_SUPER_VERSION; - - if ((temp.block_size - 1) & temp.block_size) - return SQFS_ERROR_SUPER_BLOCK_SIZE; - - if (temp.block_size < SQFS_MIN_BLOCK_SIZE) - return SQFS_ERROR_SUPER_BLOCK_SIZE; - - if (temp.block_size > SQFS_MAX_BLOCK_SIZE) - return SQFS_ERROR_SUPER_BLOCK_SIZE; - - if (temp.block_log < 12 || temp.block_log > 20) - return SQFS_ERROR_CORRUPTED; - - block_size = 1; - - for (i = 0; i < temp.block_log; ++i) - block_size <<= 1; - - if (temp.block_size != block_size) - return SQFS_ERROR_CORRUPTED; - - if (temp.compression_id < SQFS_COMP_MIN || - temp.compression_id > SQFS_COMP_MAX) - return SQFS_ERROR_UNSUPPORTED; - - if (temp.id_count == 0) - return SQFS_ERROR_CORRUPTED; - - memcpy(super, &temp, sizeof(temp)); - return 0; -} diff --git a/lib/sqfs/read_table.c b/lib/sqfs/read_table.c deleted file mode 100644 index c6a9bbe..0000000 --- a/lib/sqfs/read_table.c +++ /dev/null @@ -1,91 +0,0 @@ -/* SPDX-License-Identifier: LGPL-3.0-or-later */ -/* - * read_table.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#define SQFS_BUILDING_DLL -#include "config.h" - -#include "sqfs/meta_reader.h" -#include "sqfs/error.h" -#include "sqfs/table.h" -#include "sqfs/block.h" -#include "sqfs/io.h" -#include "util/util.h" - -#include - -int sqfs_read_table(sqfs_file_t *file, sqfs_compressor_t *cmp, - size_t table_size, sqfs_u64 location, sqfs_u64 lower_limit, - sqfs_u64 upper_limit, void **out) -{ - size_t diff, block_count, blk_idx = 0; - sqfs_u64 start, *locations; - sqfs_meta_reader_t *m; - void *data, *ptr; - int err; - - data = malloc(table_size); - if (data == NULL) - return SQFS_ERROR_ALLOC; - - /* restore list from image */ - block_count = table_size / SQFS_META_BLOCK_SIZE; - - if ((table_size % SQFS_META_BLOCK_SIZE) != 0) - ++block_count; - - locations = alloc_array(sizeof(sqfs_u64), block_count); - - if (locations == NULL) { - err = SQFS_ERROR_ALLOC; - goto fail_data; - } - - err = file->read_at(file, location, locations, - sizeof(sqfs_u64) * block_count); - if (err) - goto fail_idx; - - /* Read the actual data */ - m = sqfs_meta_reader_create(file, cmp, lower_limit, upper_limit); - if (m == NULL) { - err = SQFS_ERROR_ALLOC; - goto fail_idx; - } - - ptr = data; - - while (table_size > 0) { - start = le64toh(locations[blk_idx++]); - - err = sqfs_meta_reader_seek(m, start, 0); - if (err) - goto fail; - - diff = SQFS_META_BLOCK_SIZE; - if (diff > table_size) - diff = table_size; - - err = sqfs_meta_reader_read(m, ptr, diff); - if (err) - goto fail; - - ptr = (char *)ptr + diff; - table_size -= diff; - } - - sqfs_drop(m); - free(locations); - *out = data; - return 0; -fail: - sqfs_drop(m); -fail_idx: - free(locations); -fail_data: - free(data); - *out = NULL; - return err; -} diff --git a/lib/sqfs/readdir.c b/lib/sqfs/readdir.c deleted file mode 100644 index e2dbcd4..0000000 --- a/lib/sqfs/readdir.c +++ /dev/null @@ -1,161 +0,0 @@ -/* SPDX-License-Identifier: LGPL-3.0-or-later */ -/* - * readdir.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#define SQFS_BUILDING_DLL -#include "config.h" - -#include "sqfs/meta_reader.h" -#include "sqfs/error.h" -#include "sqfs/super.h" -#include "sqfs/inode.h" -#include "sqfs/dir.h" -#include "compat.h" - -#include -#include - -int sqfs_meta_reader_read_dir_header(sqfs_meta_reader_t *m, - sqfs_dir_header_t *hdr) -{ - int err = sqfs_meta_reader_read(m, hdr, sizeof(*hdr)); - if (err) - return err; - - hdr->count = le32toh(hdr->count); - hdr->start_block = le32toh(hdr->start_block); - hdr->inode_number = le32toh(hdr->inode_number); - - if (hdr->count > (SQFS_MAX_DIR_ENT - 1)) - return SQFS_ERROR_CORRUPTED; - - return 0; -} - -int sqfs_meta_reader_read_dir_ent(sqfs_meta_reader_t *m, - sqfs_dir_entry_t **result) -{ - sqfs_dir_entry_t ent, *out; - sqfs_u16 *diff_u16; - int err; - - err = sqfs_meta_reader_read(m, &ent, sizeof(ent)); - if (err) - return err; - - diff_u16 = (sqfs_u16 *)&ent.inode_diff; - *diff_u16 = le16toh(*diff_u16); - - ent.offset = le16toh(ent.offset); - ent.type = le16toh(ent.type); - ent.size = le16toh(ent.size); - - out = calloc(1, sizeof(*out) + ent.size + 2); - if (out == NULL) - return SQFS_ERROR_ALLOC; - - *out = ent; - err = sqfs_meta_reader_read(m, out->name, ent.size + 1); - if (err) { - free(out); - return err; - } - - *result = out; - return 0; -} - -int sqfs_readdir_state_init(sqfs_readdir_state_t *s, const sqfs_super_t *super, - const sqfs_inode_generic_t *inode) -{ - memset(s, 0, sizeof(*s)); - - if (inode->base.type == SQFS_INODE_DIR) { - s->init.block = inode->data.dir.start_block; - s->init.offset = inode->data.dir.offset; - s->init.size = inode->data.dir.size; - } else if (inode->base.type == SQFS_INODE_EXT_DIR) { - s->init.block = inode->data.dir_ext.start_block; - s->init.offset = inode->data.dir_ext.offset; - s->init.size = inode->data.dir_ext.size; - } else { - return SQFS_ERROR_NOT_DIR; - } - - s->init.block += super->directory_table_start; - s->current = s->init; - return 0; -} - -int sqfs_meta_reader_readdir(sqfs_meta_reader_t *m, sqfs_readdir_state_t *it, - sqfs_dir_entry_t **ent, - sqfs_u32 *inum, sqfs_u64 *iref) -{ - size_t count; - int ret; - - if (it->entries == 0) { - sqfs_dir_header_t hdr; - - if (it->current.size <= sizeof(hdr)) - goto out_eof; - - ret = sqfs_meta_reader_seek(m, it->current.block, - it->current.offset); - if (ret != 0) - return ret; - - ret = sqfs_meta_reader_read_dir_header(m, &hdr); - if (ret != 0) - return ret; - - sqfs_meta_reader_get_position(m, &it->current.block, - &it->current.offset); - - it->current.size -= sizeof(hdr); - it->entries = hdr.count + 1; - it->inum_base = hdr.inode_number; - it->inode_block = hdr.start_block; - } - - if (it->current.size <= sizeof(**ent)) - goto out_eof; - - ret = sqfs_meta_reader_seek(m, it->current.block, it->current.offset); - if (ret != 0) - return ret; - - ret = sqfs_meta_reader_read_dir_ent(m, ent); - if (ret) - return ret; - - sqfs_meta_reader_get_position(m, &it->current.block, - &it->current.offset); - - it->current.size -= sizeof(**ent); - it->entries -= 1; - - count = (*ent)->size + 1; - - if (count >= it->current.size) { - it->current.size = 0; - } else { - it->current.size -= count; - } - - if (inum != NULL) - *inum = it->inum_base + (*ent)->inode_diff; - - if (iref != NULL) { - *iref = (sqfs_u64)it->inode_block << 16UL; - *iref |= (*ent)->offset; - } - - return 0; -out_eof: - it->current.size = 0; - it->entries = 0; - return 1; -} diff --git a/lib/sqfs/src/block_processor/backend.c b/lib/sqfs/src/block_processor/backend.c new file mode 100644 index 0000000..b443c9d --- /dev/null +++ b/lib/sqfs/src/block_processor/backend.c @@ -0,0 +1,335 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * backend.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#define SQFS_BUILDING_DLL +#include "internal.h" + +static int set_block_size(sqfs_inode_generic_t **inode, + sqfs_u32 index, sqfs_u32 size) +{ + size_t min_size = (index + 1) * sizeof(sqfs_u32); + size_t avail = (*inode)->payload_bytes_available; + sqfs_inode_generic_t *new; + size_t newsz; + + if (avail < min_size) { + newsz = avail ? avail : (sizeof(sqfs_u32) * 4); + while (newsz < min_size) + newsz *= 2; + + if (SZ_ADD_OV(newsz, sizeof(**inode), &newsz)) + return SQFS_ERROR_OVERFLOW; + + if (sizeof(size_t) > sizeof(sqfs_u32)) { + if ((newsz - sizeof(**inode)) > 0x0FFFFFFFFUL) + return SQFS_ERROR_OVERFLOW; + } + + new = realloc((*inode), newsz); + if (new == NULL) + return SQFS_ERROR_ALLOC; + + (*inode) = new; + (*inode)->payload_bytes_available = newsz - sizeof(**inode); + } + + (*inode)->extra[index] = size; + + if (min_size >= (*inode)->payload_bytes_used) + (*inode)->payload_bytes_used = min_size; + + return 0; +} + +static void release_old_block(sqfs_block_processor_t *proc, sqfs_block_t *blk) +{ + blk->next = proc->free_list; + proc->free_list = blk; + + proc->backlog -= 1; +} + +static int process_completed_block(sqfs_block_processor_t *proc, sqfs_block_t *blk) +{ + sqfs_u64 location; + sqfs_u32 size; + int err; + + if (blk->flags & SQFS_BLK_FRAGMENT_BLOCK) { + sqfs_block_t *it = proc->fblk_in_flight, *prev = NULL; + + while (it != NULL && it->index != blk->index) { + prev = it; + it = it->next; + } + + if (it != NULL) { + if (prev == NULL) { + proc->fblk_in_flight = it->next; + } else { + prev->next = it->next; + } + free(it); + } + } + + err = proc->wr->write_data_block(proc->wr, blk->user, blk->size, + blk->checksum, + blk->flags & ~BLK_FLAG_INTERNAL, + blk->data, &location); + if (err) + goto out; + + proc->stats.output_bytes_generated += blk->size; + + if (blk->flags & SQFS_BLK_IS_SPARSE) { + if (blk->inode != NULL) { + sqfs_inode_make_extended(*(blk->inode)); + (*(blk->inode))->data.file_ext.sparse += blk->size; + + err = set_block_size(blk->inode, blk->index, 0); + if (err) + goto out; + } + proc->stats.sparse_block_count += 1; + } else if (blk->size != 0) { + size = blk->size; + if (!(blk->flags & SQFS_BLK_IS_COMPRESSED)) + size |= 1 << 24; + + if (blk->flags & SQFS_BLK_FRAGMENT_BLOCK) { + if (proc->frag_tbl != NULL) { + err = sqfs_frag_table_set(proc->frag_tbl, + blk->index, location, + size); + if (err) + goto out; + } + proc->stats.frag_block_count += 1; + } else { + if (blk->inode != NULL) { + err = set_block_size(blk->inode, blk->index, + size); + if (err) + goto out; + } + proc->stats.data_block_count += 1; + } + } + + if (blk->flags & SQFS_BLK_LAST_BLOCK && blk->inode != NULL) + sqfs_inode_set_file_block_start(*(blk->inode), location); +out: + release_old_block(proc, blk); + return err; +} + +static int process_completed_fragment(sqfs_block_processor_t *proc, + sqfs_block_t *frag) +{ + chunk_info_t *chunk = NULL, search; + struct hash_entry *entry; + sqfs_u32 index, offset; + int err; + + if (frag->flags & SQFS_BLK_IS_SPARSE) { + if (frag->inode != NULL) { + sqfs_inode_make_extended(*(frag->inode)); + set_block_size(frag->inode, frag->index, 0); + (*(frag->inode))->data.file_ext.sparse += frag->size; + } + proc->stats.sparse_block_count += 1; + release_old_block(proc, frag); + return 0; + } + + proc->stats.total_frag_count += 1; + + if (!(frag->flags & SQFS_BLK_DONT_DEDUPLICATE)) { + search.hash = frag->checksum; + search.size = frag->size; + + proc->current_frag = frag; + proc->fblk_lookup_error = 0; + entry = hash_table_search_pre_hashed(proc->frag_ht, + search.hash, &search); + proc->current_frag = NULL; + + if (proc->fblk_lookup_error != 0) { + err = proc->fblk_lookup_error; + goto fail; + } + + if (entry != NULL) { + if (frag->inode != NULL) { + chunk = entry->data; + sqfs_inode_set_frag_location(*(frag->inode), + chunk->index, + chunk->offset); + } + release_old_block(proc, frag); + return 0; + } + } + + if (proc->frag_block != NULL) { + size_t size = proc->frag_block->size + frag->size; + + if (size > proc->max_block_size) { + proc->frag_block->io_seq_num = proc->io_seq_num++; + + err = enqueue_block(proc, proc->frag_block); + proc->frag_block = NULL; + + if (err) + goto fail; + } + } + + if (proc->frag_block == NULL) { + if (proc->frag_tbl == NULL) { + index = 0; + } else { + err = sqfs_frag_table_append(proc->frag_tbl, + 0, 0, &index); + if (err) + goto fail; + } + + offset = 0; + proc->frag_block = frag; + proc->frag_block->index = index; + proc->frag_block->flags &= + (SQFS_BLK_DONT_COMPRESS | SQFS_BLK_ALIGN); + proc->frag_block->flags |= SQFS_BLK_FRAGMENT_BLOCK; + } else { + index = proc->frag_block->index; + offset = proc->frag_block->size; + + memcpy(proc->frag_block->data + proc->frag_block->size, + frag->data, frag->size); + + proc->frag_block->size += frag->size; + proc->frag_block->flags |= + (frag->flags & + (SQFS_BLK_DONT_COMPRESS | SQFS_BLK_ALIGN)); + } + + if (proc->frag_tbl != NULL) { + err = SQFS_ERROR_ALLOC; + chunk = calloc(1, sizeof(*chunk)); + if (chunk == NULL) + goto fail; + + chunk->index = index; + chunk->offset = offset; + chunk->size = frag->size; + chunk->hash = frag->checksum; + + proc->current_frag = frag; + proc->fblk_lookup_error = 0; + entry = hash_table_insert_pre_hashed(proc->frag_ht, chunk->hash, + chunk, chunk); + proc->current_frag = NULL; + + if (proc->fblk_lookup_error != 0) { + err = proc->fblk_lookup_error; + goto fail; + } + + if (entry == NULL) + goto fail; + } + + if (frag->inode != NULL) + sqfs_inode_set_frag_location(*(frag->inode), index, offset); + + if (frag != proc->frag_block) + release_old_block(proc, frag); + + proc->stats.actual_frag_count += 1; + return 0; +fail: + free(chunk); + if (frag != proc->frag_block) + release_old_block(proc, frag); + return err; +} + +static void store_io_block(sqfs_block_processor_t *proc, sqfs_block_t *blk) +{ + sqfs_block_t *prev = NULL, *it = proc->io_queue; + + while (it != NULL && (it->io_seq_num < blk->io_seq_num)) { + prev = it; + it = it->next; + } + + if (prev == NULL) { + proc->io_queue = blk; + } else { + prev->next = blk; + } + + blk->next = it; +} + +int dequeue_block(sqfs_block_processor_t *proc) +{ + size_t backlog_old = proc->backlog; + sqfs_block_t *blk; + int status; + + do { + while (proc->io_queue != NULL) { + if (proc->io_queue->io_seq_num != proc->io_deq_seq_num) + break; + + blk = proc->io_queue; + proc->io_queue = blk->next; + proc->io_deq_seq_num += 1; + + status = process_completed_block(proc, blk); + if (status != 0) + return status; + } + + if (proc->backlog < backlog_old) + break; + + if ((proc->backlog == 1) && + (proc->frag_block != NULL || proc->blk_current != NULL)) { + break; + } + + if ((proc->backlog == 2) && + proc->frag_block != NULL && proc->blk_current != NULL) { + break; + } + + blk = proc->pool->dequeue(proc->pool); + + if (blk == NULL) { + status = proc->pool->get_status(proc->pool); + return status ? status : SQFS_ERROR_INTERNAL; + } + + if (blk->flags & SQFS_BLK_IS_FRAGMENT) { + status = process_completed_fragment(proc, blk); + if (status != 0) + return status; + } else { + if (!(blk->flags & SQFS_BLK_FRAGMENT_BLOCK) || + (blk->flags & BLK_FLAG_MANUAL_SUBMISSION)) { + blk->io_seq_num = proc->io_seq_num++; + } + + store_io_block(proc, blk); + } + } while (proc->backlog >= backlog_old); + + return 0; +} diff --git a/lib/sqfs/src/block_processor/block_processor.c b/lib/sqfs/src/block_processor/block_processor.c new file mode 100644 index 0000000..d607437 --- /dev/null +++ b/lib/sqfs/src/block_processor/block_processor.c @@ -0,0 +1,358 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * block_processor.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#define SQFS_BUILDING_DLL +#include "internal.h" + +static int process_block(void *userptr, void *workitem) +{ + worker_data_t *worker = userptr; + sqfs_block_t *block = workitem; + sqfs_s32 ret; + + if (block->size == 0) + return 0; + + if (!(block->flags & SQFS_BLK_IGNORE_SPARSE) && + is_memory_zero(block->data, block->size)) { + block->flags |= SQFS_BLK_IS_SPARSE; + return 0; + } + + if (block->flags & SQFS_BLK_DONT_HASH) { + block->checksum = 0; + } else { + block->checksum = xxh32(block->data, block->size); + } + + if (block->flags & (SQFS_BLK_IS_FRAGMENT | SQFS_BLK_DONT_COMPRESS)) + return 0; + + ret = worker->cmp->do_block(worker->cmp, block->data, block->size, + worker->scratch, worker->scratch_size); + if (ret < 0) + return ret; + + if (ret > 0) { + memcpy(block->data, worker->scratch, ret); + block->size = ret; + block->flags |= SQFS_BLK_IS_COMPRESSED; + } + return 0; +} + +static int load_frag_block(sqfs_block_processor_t *proc, sqfs_u32 index) +{ + sqfs_fragment_t info; + size_t size; + int ret; + + if (proc->cached_frag_blk == NULL) { + size = sizeof(*proc->cached_frag_blk); + + proc->cached_frag_blk = alloc_flex(size, 1, + proc->max_block_size); + + if (proc->cached_frag_blk == NULL) + return SQFS_ERROR_ALLOC; + } else { + if (proc->cached_frag_blk->index == index) + return 0; + } + + ret = sqfs_frag_table_lookup(proc->frag_tbl, index, &info); + if (ret != 0) + return ret; + + size = SQFS_ON_DISK_BLOCK_SIZE(info.size); + if (size > proc->max_block_size) + return SQFS_ERROR_CORRUPTED; + + if (SQFS_IS_BLOCK_COMPRESSED(info.size)) { + ret = proc->file->read_at(proc->file, info.start_offset, + proc->scratch, size); + if (ret != 0) + return ret; + + ret = proc->uncmp->do_block(proc->uncmp, proc->scratch, size, + proc->cached_frag_blk->data, + proc->max_block_size); + if (ret <= 0) + return ret ? ret : SQFS_ERROR_OVERFLOW; + + size = ret; + } else { + ret = proc->file->read_at(proc->file, info.start_offset, + proc->cached_frag_blk->data, size); + if (ret != 0) + return ret; + } + + proc->cached_frag_blk->size = size; + proc->cached_frag_blk->index = index; + return 0; +} + +static bool chunk_info_equals(void *user, const void *k, const void *c) +{ + const chunk_info_t *key = k, *cmp = c; + sqfs_block_processor_t *proc = user; + sqfs_block_t *it; + int ret; + + if (key->size != cmp->size || key->hash != cmp->hash) + return false; + + if (proc->uncmp == NULL || proc->file == NULL) + return true; + + if (proc->current_frag == NULL || proc->frag_tbl == NULL) + return true; + + if (proc->fblk_lookup_error != 0) + return false; + + for (it = proc->fblk_in_flight; it != NULL; it = it->next) { + if (it->index == cmp->index) + break; + } + + if (it == NULL && proc->frag_block != NULL) { + if (proc->frag_block->index == cmp->index) + it = proc->frag_block; + } + + if (it == NULL) { + ret = load_frag_block(proc, cmp->index); + if (ret != 0) { + proc->fblk_lookup_error = ret; + return false; + } + + it = proc->cached_frag_blk; + } + + if (cmp->offset >= it->size || (it->size - cmp->offset) < cmp->size) { + proc->fblk_lookup_error = SQFS_ERROR_CORRUPTED; + return false; + } + + if (cmp->size != proc->current_frag->size) { + proc->fblk_lookup_error = SQFS_ERROR_CORRUPTED; + return false; + } + + return memcmp(it->data + cmp->offset, + proc->current_frag->data, cmp->size) == 0; +} + +static void ht_delete_function(struct hash_entry *entry) +{ + free(entry->data); +} + +static void free_block_list(sqfs_block_t *list) +{ + while (list != NULL) { + sqfs_block_t *it = list; + list = it->next; + free(it); + } +} + +static void block_processor_destroy(sqfs_object_t *base) +{ + sqfs_block_processor_t *proc = (sqfs_block_processor_t *)base; + + free(proc->frag_block); + free(proc->blk_current); + free(proc->cached_frag_blk); + + free_block_list(proc->free_list); + free_block_list(proc->io_queue); + free_block_list(proc->fblk_in_flight); + + if (proc->frag_ht != NULL) + hash_table_destroy(proc->frag_ht, ht_delete_function); + + /* XXX: shut down the pool first before cleaning up the worker data */ + if (proc->pool != NULL) + proc->pool->destroy(proc->pool); + + while (proc->workers != NULL) { + worker_data_t *worker = proc->workers; + proc->workers = worker->next; + + sqfs_drop(worker->cmp); + free(worker); + } + + sqfs_drop(proc->frag_tbl); + sqfs_drop(proc->wr); + sqfs_drop(proc->file); + sqfs_drop(proc->uncmp); + free(proc); +} + +int sqfs_block_processor_sync(sqfs_block_processor_t *proc) +{ + int ret; + + for (;;) { + if (proc->backlog == 0) + break; + + if ((proc->backlog == 1) && + (proc->frag_block != NULL || proc->blk_current != NULL)) { + break; + } + + if ((proc->backlog == 2) && + proc->frag_block != NULL && proc->blk_current != NULL) { + break; + } + + ret = dequeue_block(proc); + if (ret != 0) + return ret; + } + + return 0; +} + +int sqfs_block_processor_finish(sqfs_block_processor_t *proc) +{ + sqfs_block_t *blk; + int status; + + status = sqfs_block_processor_sync(proc); + if (status != 0) + return status; + + if (proc->frag_block != NULL) { + blk = proc->frag_block; + blk->next = NULL; + proc->frag_block = NULL; + + blk->io_seq_num = proc->io_seq_num++; + + status = enqueue_block(proc, blk); + if (status != 0) + return status; + + status = sqfs_block_processor_sync(proc); + } + + return status; +} + +const sqfs_block_processor_stats_t +*sqfs_block_processor_get_stats(const sqfs_block_processor_t *proc) +{ + return &proc->stats; +} + +int sqfs_block_processor_create_ex(const sqfs_block_processor_desc_t *desc, + sqfs_block_processor_t **out) +{ + size_t i, count, scratch_size = 0; + sqfs_block_processor_t *proc; + int ret; + + if (desc->size != sizeof(sqfs_block_processor_desc_t)) + return SQFS_ERROR_ARG_INVALID; + + if (desc->file != NULL && desc->uncmp != NULL) + scratch_size = desc->max_block_size; + + proc = alloc_flex(sizeof(*proc), 1, scratch_size); + if (proc == NULL) + return SQFS_ERROR_ALLOC; + + sqfs_object_init(proc, block_processor_destroy, NULL); + + proc->max_backlog = desc->max_backlog; + proc->max_block_size = desc->max_block_size; + proc->frag_tbl = sqfs_grab(desc->tbl); + proc->wr = sqfs_grab(desc->wr); + proc->file = sqfs_grab(desc->file); + proc->uncmp = sqfs_grab(desc->uncmp); + proc->stats.size = sizeof(proc->stats); + + /* we need at least one current data block + one fragment block */ + if (proc->max_backlog < 3) + proc->max_backlog = 3; + + /* create the thread pool */ + proc->pool = thread_pool_create(desc->num_workers, process_block); + if (proc->pool == NULL) { + ret = SQFS_ERROR_INTERNAL; + goto fail_pool; + } + + /* create the worker compressors & scratch buffer */ + count = proc->pool->get_worker_count(proc->pool); + + for (i = 0; i < count; ++i) { + worker_data_t *worker = alloc_flex(sizeof(*worker), 1, + desc->max_block_size); + if (worker == NULL) { + ret = SQFS_ERROR_ALLOC; + goto fail_pool; + } + + worker->scratch_size = desc->max_block_size; + worker->next = proc->workers; + proc->workers = worker; + + worker->cmp = sqfs_copy(desc->cmp); + if (worker->cmp == NULL) { + ret = SQFS_ERROR_ALLOC; + goto fail_pool; + } + + proc->pool->set_worker_ptr(proc->pool, i, worker); + } + + /* create the fragment hash table */ + proc->frag_ht = hash_table_create(NULL, chunk_info_equals); + if (proc->frag_ht == NULL) { + ret = SQFS_ERROR_ALLOC; + goto fail_pool; + } + + proc->frag_ht->user = proc; + *out = proc; + return 0; +fail_pool: + block_processor_destroy((sqfs_object_t *)proc); + return ret; +} + +sqfs_block_processor_t *sqfs_block_processor_create(size_t max_block_size, + sqfs_compressor_t *cmp, + unsigned int num_workers, + size_t max_backlog, + sqfs_block_writer_t *wr, + sqfs_frag_table_t *tbl) +{ + sqfs_block_processor_desc_t desc; + sqfs_block_processor_t *out; + + memset(&desc, 0, sizeof(desc)); + desc.size = sizeof(desc); + desc.max_block_size = max_block_size; + desc.num_workers = num_workers; + desc.max_backlog = max_backlog; + desc.cmp = cmp; + desc.wr = wr; + desc.tbl = tbl; + + if (sqfs_block_processor_create_ex(&desc, &out) != 0) + return NULL; + + return out; +} diff --git a/lib/sqfs/src/block_processor/frontend.c b/lib/sqfs/src/block_processor/frontend.c new file mode 100644 index 0000000..e8a4207 --- /dev/null +++ b/lib/sqfs/src/block_processor/frontend.c @@ -0,0 +1,243 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * frontend.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#define SQFS_BUILDING_DLL +#include "internal.h" + +static int get_new_block(sqfs_block_processor_t *proc, sqfs_block_t **out) +{ + sqfs_block_t *blk; + + while (proc->backlog >= proc->max_backlog) { + int ret = dequeue_block(proc); + if (ret != 0) + return ret; + } + + if (proc->free_list != NULL) { + blk = proc->free_list; + proc->free_list = blk->next; + } else { + blk = malloc(sizeof(*blk) + proc->max_block_size); + if (blk == NULL) + return SQFS_ERROR_ALLOC; + } + + memset(blk, 0, sizeof(*blk)); + *out = blk; + + proc->backlog += 1; + return 0; +} + +static int add_sentinel_block(sqfs_block_processor_t *proc) +{ + sqfs_block_t *blk; + int ret; + + ret = get_new_block(proc, &blk); + if (ret != 0) + return ret; + + blk->inode = proc->inode; + blk->flags = proc->blk_flags | SQFS_BLK_LAST_BLOCK; + + return enqueue_block(proc, blk); +} + +int enqueue_block(sqfs_block_processor_t *proc, sqfs_block_t *blk) +{ + int status; + + if ((blk->flags & SQFS_BLK_FRAGMENT_BLOCK) && + proc->file != NULL && proc->uncmp != NULL) { + sqfs_block_t *copy = alloc_flex(sizeof(*copy), 1, blk->size); + + if (copy == NULL) + return SQFS_ERROR_ALLOC; + + copy->size = blk->size; + copy->index = blk->index; + memcpy(copy->data, blk->data, blk->size); + + copy->next = proc->fblk_in_flight; + proc->fblk_in_flight = copy; + } + + if (proc->pool->submit(proc->pool, blk) != 0) { + status = proc->pool->get_status(proc->pool); + + if (status == 0) + status = SQFS_ERROR_ALLOC; + + blk->next = proc->free_list; + proc->free_list = blk; + return status; + } + + return 0; +} + +int sqfs_block_processor_begin_file(sqfs_block_processor_t *proc, + sqfs_inode_generic_t **inode, + void *user, sqfs_u32 flags) +{ + if (proc->begin_called) + return SQFS_ERROR_SEQUENCE; + + if (flags & ~SQFS_BLK_USER_SETTABLE_FLAGS) + return SQFS_ERROR_UNSUPPORTED; + + if (inode != NULL) { + (*inode) = calloc(1, sizeof(sqfs_inode_generic_t)); + if ((*inode) == NULL) + return SQFS_ERROR_ALLOC; + + (*inode)->base.type = SQFS_INODE_FILE; + sqfs_inode_set_frag_location(*inode, 0xFFFFFFFF, 0xFFFFFFFF); + } + + proc->begin_called = true; + proc->inode = inode; + proc->blk_flags = flags | SQFS_BLK_FIRST_BLOCK; + proc->blk_index = 0; + proc->user = user; + return 0; +} + +int sqfs_block_processor_append(sqfs_block_processor_t *proc, const void *data, + size_t size) +{ + sqfs_block_t *new; + sqfs_u64 filesize; + size_t diff; + int err; + + if (!proc->begin_called) + return SQFS_ERROR_SEQUENCE; + + if (proc->inode != NULL) { + sqfs_inode_get_file_size(*(proc->inode), &filesize); + sqfs_inode_set_file_size(*(proc->inode), filesize + size); + } + + while (size > 0) { + if (proc->blk_current == NULL) { + err = get_new_block(proc, &new); + if (err != 0) + return err; + + proc->blk_current = new; + proc->blk_current->flags = proc->blk_flags; + proc->blk_current->inode = proc->inode; + proc->blk_current->user = proc->user; + proc->blk_current->index = proc->blk_index++; + proc->blk_flags &= ~SQFS_BLK_FIRST_BLOCK; + } + + diff = proc->max_block_size - proc->blk_current->size; + + if (diff == 0) { + err = enqueue_block(proc, proc->blk_current); + proc->blk_current = NULL; + + if (err) + return err; + continue; + } + + if (diff > size) + diff = size; + + memcpy(proc->blk_current->data + proc->blk_current->size, + data, diff); + + size -= diff; + proc->blk_current->size += diff; + data = (const char *)data + diff; + + proc->stats.input_bytes_read += diff; + } + + if (proc->blk_current->size == proc->max_block_size) { + err = enqueue_block(proc, proc->blk_current); + proc->blk_current = NULL; + + if (err) + return err; + } + + return 0; +} + +int sqfs_block_processor_end_file(sqfs_block_processor_t *proc) +{ + int err; + + if (!proc->begin_called) + return SQFS_ERROR_SEQUENCE; + + if (proc->blk_current == NULL) { + if (!(proc->blk_flags & SQFS_BLK_FIRST_BLOCK)) { + err = add_sentinel_block(proc); + if (err) + return err; + } + } else { + if (proc->blk_flags & SQFS_BLK_DONT_FRAGMENT) { + proc->blk_current->flags |= SQFS_BLK_LAST_BLOCK; + } else { + if (!(proc->blk_current->flags & + SQFS_BLK_FIRST_BLOCK)) { + err = add_sentinel_block(proc); + if (err) + return err; + } + + proc->blk_current->flags |= SQFS_BLK_IS_FRAGMENT; + } + + err = enqueue_block(proc, proc->blk_current); + proc->blk_current = NULL; + + if (err) + return err; + } + + proc->begin_called = false; + proc->inode = NULL; + proc->user = NULL; + proc->blk_flags = 0; + return 0; +} + +int sqfs_block_processor_submit_block(sqfs_block_processor_t *proc, void *user, + sqfs_u32 flags, const void *data, + size_t size) +{ + sqfs_block_t *blk; + int ret; + + if (proc->begin_called) + return SQFS_ERROR_SEQUENCE; + + if (size > proc->max_block_size) + return SQFS_ERROR_OVERFLOW; + + if (flags & ~SQFS_BLK_FLAGS_ALL) + return SQFS_ERROR_UNSUPPORTED; + + ret = get_new_block(proc, &blk); + if (ret != 0) + return ret; + + blk->flags = flags | BLK_FLAG_MANUAL_SUBMISSION; + blk->user = user; + blk->size = size; + memcpy(blk->data, data, size); + + return enqueue_block(proc, blk); +} diff --git a/lib/sqfs/src/block_processor/internal.h b/lib/sqfs/src/block_processor/internal.h new file mode 100644 index 0000000..0b2c88d --- /dev/null +++ b/lib/sqfs/src/block_processor/internal.h @@ -0,0 +1,115 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * internal.h + * + * Copyright (C) 2019 David Oberhollenzer + */ +#ifndef INTERNAL_H +#define INTERNAL_H + +#include "config.h" + +#include "sqfs/block_processor.h" +#include "sqfs/block_writer.h" +#include "sqfs/frag_table.h" +#include "sqfs/compressor.h" +#include "sqfs/inode.h" +#include "sqfs/table.h" +#include "sqfs/error.h" +#include "sqfs/block.h" +#include "sqfs/io.h" + +#include "util/hash_table.h" +#include "util/threadpool.h" +#include "util/util.h" + +#include +#include + +typedef struct { + sqfs_u32 index; + sqfs_u32 offset; + sqfs_u32 size; + sqfs_u32 hash; +} chunk_info_t; + +enum { + BLK_FLAG_MANUAL_SUBMISSION = 0x10000000, + BLK_FLAG_INTERNAL = 0x10000000, +}; + +typedef struct sqfs_block_t { + struct sqfs_block_t *next; + sqfs_inode_generic_t **inode; + + sqfs_u32 io_seq_num; + sqfs_u32 flags; + sqfs_u32 size; + sqfs_u32 checksum; + + /* For data blocks: index within the inode. + For fragment fragment blocks: fragment table index. */ + sqfs_u32 index; + + /* User data pointer */ + void *user; + + sqfs_u8 data[]; +} sqfs_block_t; + +typedef struct worker_data_t { + struct worker_data_t *next; + sqfs_compressor_t *cmp; + + size_t scratch_size; + sqfs_u8 scratch[]; +} worker_data_t; + +struct sqfs_block_processor_t { + sqfs_object_t obj; + + sqfs_frag_table_t *frag_tbl; + sqfs_block_t *frag_block; + sqfs_block_writer_t *wr; + + sqfs_block_processor_stats_t stats; + + sqfs_inode_generic_t **inode; + sqfs_block_t *blk_current; + sqfs_u32 blk_flags; + sqfs_u32 blk_index; + void *user; + + struct hash_table *frag_ht; + sqfs_block_t *free_list; + + size_t max_block_size; + size_t max_backlog; + size_t backlog; + + bool begin_called; + + sqfs_file_t *file; + sqfs_compressor_t *uncmp; + + thread_pool_t *pool; + worker_data_t *workers; + + sqfs_block_t *io_queue; + sqfs_u32 io_seq_num; + sqfs_u32 io_deq_seq_num; + + sqfs_block_t *current_frag; + sqfs_block_t *cached_frag_blk; + sqfs_block_t *fblk_in_flight; + int fblk_lookup_error; + + sqfs_u8 scratch[]; +}; + +SQFS_INTERNAL int enqueue_block(sqfs_block_processor_t *proc, + sqfs_block_t *blk); + +SQFS_INTERNAL int dequeue_block(sqfs_block_processor_t *proc); + +#endif /* INTERNAL_H */ diff --git a/lib/sqfs/src/block_writer.c b/lib/sqfs/src/block_writer.c new file mode 100644 index 0000000..a5135bc --- /dev/null +++ b/lib/sqfs/src/block_writer.c @@ -0,0 +1,238 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * block_writer.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#define SQFS_BUILDING_DLL +#include "config.h" + +#include "sqfs/block_writer.h" +#include "sqfs/error.h" +#include "sqfs/block.h" +#include "sqfs/io.h" + +#include "util/array.h" +#include "util/util.h" + +#include +#include + +#define MK_BLK_HASH(chksum, size) \ + (((sqfs_u64)(size) << 32) | (sqfs_u64)(chksum)) + +#define SIZE_FROM_HASH(hash) ((hash >> 32) & ((1 << 24) - 1)) + +#define INIT_BLOCK_COUNT (128) +#define SCRATCH_SIZE (8192) + +typedef struct { + sqfs_u64 offset; + sqfs_u64 hash; +} blk_info_t; + +typedef struct { + sqfs_block_writer_t base; + sqfs_file_t *file; + + array_t blocks; + size_t devblksz; + + size_t file_start; + + sqfs_u32 flags; + + sqfs_u8 scratch[]; +} block_writer_default_t; + +static int store_block_location(block_writer_default_t *wr, sqfs_u64 offset, + sqfs_u32 size, sqfs_u32 chksum) +{ + blk_info_t info = { offset, MK_BLK_HASH(chksum, size) }; + + return array_append(&(wr->blocks), &info); +} + +static int deduplicate_blocks(block_writer_default_t *wr, sqfs_u32 flags, sqfs_u64 *out) +{ + const blk_info_t *blocks = wr->blocks.data; + sqfs_u64 loc_a, loc_b, sz; + size_t i, j, count; + int ret; + + count = wr->blocks.used - wr->file_start; + if (count == 0) { + *out = 0; + return 0; + } + + if (flags & SQFS_BLK_DONT_DEDUPLICATE) { + *out = blocks[wr->file_start].offset; + return 0; + } + + sz = 0; + loc_a = blocks[wr->file_start].offset; + + for (i = 0; i < count; ++i) + sz += SIZE_FROM_HASH(blocks[wr->file_start + i].hash); + + for (i = 0; i < wr->file_start; ++i) { + for (j = 0; j < count; ++j) { + if (blocks[i + j].hash == 0) + break; + + if (blocks[i + j].hash != + blocks[wr->file_start + j].hash) + break; + } + + if (j != count) + continue; + + if (wr->flags & SQFS_BLOCK_WRITER_HASH_COMPARE_ONLY) + break; + + loc_b = blocks[i].offset; + + ret = check_file_range_equal(wr->file, wr->scratch, + SCRATCH_SIZE, loc_a, loc_b, sz); + if (ret == 0) + break; + if (ret < 0) + return ret; + } + + *out = blocks[i].offset; + if (i >= wr->file_start) + return 0; + + if (count >= (wr->file_start - i)) { + wr->blocks.used = i + count; + } else { + wr->blocks.used = wr->file_start; + } + + sz = blocks[wr->blocks.used - 1].offset + + SIZE_FROM_HASH(blocks[wr->blocks.used - 1].hash); + + return wr->file->truncate(wr->file, sz); +} + +static int align_file(block_writer_default_t *wr, sqfs_u32 flags) +{ + void *padding; + sqfs_u64 size; + size_t diff; + int ret; + + if (!(flags & SQFS_BLK_ALIGN)) + return 0; + + size = wr->file->get_size(wr->file); + diff = size % wr->devblksz; + if (diff == 0) + return 0; + + padding = calloc(1, diff); + if (padding == 0) + return SQFS_ERROR_ALLOC; + + ret = wr->file->write_at(wr->file, size, padding, diff); + free(padding); + if (ret) + return ret; + + return store_block_location(wr, size, 0, 0); +} + +static void block_writer_destroy(sqfs_object_t *wr) +{ + sqfs_drop(((block_writer_default_t *)wr)->file); + array_cleanup(&(((block_writer_default_t *)wr)->blocks)); + free(wr); +} + +static int write_data_block(sqfs_block_writer_t *base, void *user, + sqfs_u32 size, sqfs_u32 checksum, sqfs_u32 flags, + const sqfs_u8 *data, sqfs_u64 *location) +{ + block_writer_default_t *wr = (block_writer_default_t *)base; + int err; + (void)user; + + if (flags & (SQFS_BLK_FIRST_BLOCK | SQFS_BLK_FRAGMENT_BLOCK)) { + err = align_file(wr, flags); + if (err) + return err; + + if (flags & SQFS_BLK_FIRST_BLOCK) + wr->file_start = wr->blocks.used; + } + + *location = wr->file->get_size(wr->file); + + if (size != 0 && !(flags & SQFS_BLK_IS_SPARSE)) { + sqfs_u32 out = size; + if (!(flags & SQFS_BLK_IS_COMPRESSED)) + out |= 1 << 24; + + err = store_block_location(wr, *location, out, checksum); + if (err) + return err; + + err = wr->file->write_at(wr->file, *location, data, size); + if (err) + return err; + } + + if (flags & (SQFS_BLK_LAST_BLOCK | SQFS_BLK_FRAGMENT_BLOCK)) { + err = align_file(wr, flags); + if (err) + return err; + + if (flags & SQFS_BLK_LAST_BLOCK) + return deduplicate_blocks(wr, flags, location); + } + + return 0; +} + +static sqfs_u64 get_block_count(const sqfs_block_writer_t *wr) +{ + return ((const block_writer_default_t *)wr)->blocks.used; +} + +sqfs_block_writer_t *sqfs_block_writer_create(sqfs_file_t *file, + size_t devblksz, sqfs_u32 flags) +{ + block_writer_default_t *wr; + + if (flags & ~SQFS_BLOCK_WRITER_ALL_FLAGS) + return NULL; + + if (flags & SQFS_BLOCK_WRITER_HASH_COMPARE_ONLY) { + wr = calloc(1, sizeof(*wr)); + } else { + wr = alloc_flex(sizeof(*wr), 1, SCRATCH_SIZE); + } + + if (wr == NULL) + return NULL; + + sqfs_object_init(wr, block_writer_destroy, NULL); + + ((sqfs_block_writer_t *)wr)->write_data_block = write_data_block; + ((sqfs_block_writer_t *)wr)->get_block_count = get_block_count; + wr->flags = flags; + wr->file = sqfs_grab(file); + wr->devblksz = devblksz; + + if (array_init(&(wr->blocks), sizeof(blk_info_t), INIT_BLOCK_COUNT)) { + sqfs_drop(wr->file); + free(wr); + return NULL; + } + + return (sqfs_block_writer_t *)wr; +} diff --git a/lib/sqfs/src/comp/compressor.c b/lib/sqfs/src/comp/compressor.c new file mode 100644 index 0000000..4c4d73c --- /dev/null +++ b/lib/sqfs/src/comp/compressor.c @@ -0,0 +1,218 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * compressor.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#define SQFS_BUILDING_DLL +#include "config.h" + +#include +#include + +#include "internal.h" + +typedef int (*compressor_fun_t)(const sqfs_compressor_config_t *cfg, + sqfs_compressor_t **out); + +static compressor_fun_t compressors[SQFS_COMP_MAX + 1] = { +#ifdef WITH_GZIP + [SQFS_COMP_GZIP] = gzip_compressor_create, +#endif +#ifdef WITH_XZ + [SQFS_COMP_XZ] = xz_compressor_create, + [SQFS_COMP_LZMA] = lzma_compressor_create, +#endif +#ifdef WITH_LZ4 + [SQFS_COMP_LZ4] = lz4_compressor_create, +#endif +#ifdef WITH_ZSTD + [SQFS_COMP_ZSTD] = zstd_compressor_create, +#endif +}; + +static const char *names[] = { + [SQFS_COMP_GZIP] = "gzip", + [SQFS_COMP_LZMA] = "lzma", + [SQFS_COMP_LZO] = "lzo", + [SQFS_COMP_XZ] = "xz", + [SQFS_COMP_LZ4] = "lz4", + [SQFS_COMP_ZSTD] = "zstd", +}; + +int sqfs_generic_write_options(sqfs_file_t *file, const void *data, size_t size) +{ + sqfs_u8 buffer[64]; + sqfs_u16 header; + int ret; + + /* XXX: options for all known compressors should fit into this */ + if (size >= (sizeof(buffer) - sizeof(header))) + return SQFS_ERROR_INTERNAL; + + header = htole16(0x8000 | size); + memcpy(buffer, &header, sizeof(header)); + memcpy(buffer + sizeof(header), data, size); + + ret = file->write_at(file, sizeof(sqfs_super_t), + buffer, sizeof(header) + size); + if (ret) + return ret; + + return sizeof(header) + size; +} + +int sqfs_generic_read_options(sqfs_file_t *file, void *data, size_t size) +{ + sqfs_u8 buffer[64]; + sqfs_u16 header; + int ret; + + /* XXX: options for all known compressors should fit into this */ + if (size >= (sizeof(buffer) - sizeof(header))) + return SQFS_ERROR_INTERNAL; + + ret = file->read_at(file, sizeof(sqfs_super_t), + buffer, sizeof(header) + size); + if (ret) + return ret; + + memcpy(&header, buffer, sizeof(header)); + + if (le16toh(header) != (0x8000 | size)) + return SQFS_ERROR_CORRUPTED; + + memcpy(data, buffer + 2, size); + return 0; +} + +int sqfs_compressor_create(const sqfs_compressor_config_t *cfg, + sqfs_compressor_t **out) +{ + sqfs_u8 padd0[sizeof(cfg->opt)]; + int ret; + + *out = NULL; + + /* check compressor ID */ + if (cfg == NULL) + return SQFS_ERROR_ARG_INVALID; + + if (cfg->id < SQFS_COMP_MIN || cfg->id > SQFS_COMP_MAX) + return SQFS_ERROR_UNSUPPORTED; + + if (compressors[cfg->id] == NULL) + return SQFS_ERROR_UNSUPPORTED; + + /* make sure the padding bytes are cleared, so we could theoretically + turn them into option fields in the future and remain compatible */ + memset(padd0, 0, sizeof(padd0)); + + switch (cfg->id) { + case SQFS_COMP_XZ: + ret = memcmp(cfg->opt.xz.padd0, padd0, + sizeof(cfg->opt.xz.padd0)); + break; + case SQFS_COMP_LZMA: + ret = memcmp(cfg->opt.lzma.padd0, padd0, + sizeof(cfg->opt.lzma.padd0)); + break; + case SQFS_COMP_LZO: + ret = memcmp(cfg->opt.lzo.padd0, padd0, + sizeof(cfg->opt.lzo.padd0)); + break; + case SQFS_COMP_GZIP: + ret = memcmp(cfg->opt.gzip.padd0, padd0, + sizeof(cfg->opt.gzip.padd0)); + break; + default: + ret = memcmp(cfg->opt.padd0, padd0, sizeof(cfg->opt.padd0)); + break; + } + + if (ret != 0) + return SQFS_ERROR_ARG_INVALID; + + return compressors[cfg->id](cfg, out); +} + +const char *sqfs_compressor_name_from_id(SQFS_COMPRESSOR id) +{ + if (id < 0 || (size_t)id >= sizeof(names) / sizeof(names[0])) + return NULL; + + return names[id]; +} + +int sqfs_compressor_id_from_name(const char *name) +{ + size_t i; + + for (i = 0; i < sizeof(names) / sizeof(names[0]); ++i) { + if (names[i] != NULL && strcmp(names[i], name) == 0) + return i; + } + + return SQFS_ERROR_UNSUPPORTED; +} + +int sqfs_compressor_config_init(sqfs_compressor_config_t *cfg, + SQFS_COMPRESSOR id, + size_t block_size, sqfs_u16 flags) +{ + sqfs_u32 flag_mask = SQFS_COMP_FLAG_GENERIC_ALL; + + memset(cfg, 0, sizeof(*cfg)); + + switch (id) { + case SQFS_COMP_GZIP: + flag_mask |= SQFS_COMP_FLAG_GZIP_ALL; + cfg->level = SQFS_GZIP_DEFAULT_LEVEL; + cfg->opt.gzip.window_size = SQFS_GZIP_DEFAULT_WINDOW; + break; + case SQFS_COMP_LZO: + cfg->opt.lzo.algorithm = SQFS_LZO_DEFAULT_ALG; + cfg->level = SQFS_LZO_DEFAULT_LEVEL; + break; + case SQFS_COMP_ZSTD: + cfg->level = SQFS_ZSTD_DEFAULT_LEVEL; + break; + case SQFS_COMP_XZ: + flag_mask |= SQFS_COMP_FLAG_XZ_ALL; + cfg->level = SQFS_XZ_DEFAULT_LEVEL; + cfg->opt.xz.dict_size = block_size; + cfg->opt.xz.lc = SQFS_XZ_DEFAULT_LC; + cfg->opt.xz.lp = SQFS_XZ_DEFAULT_LP; + cfg->opt.xz.pb = SQFS_XZ_DEFAULT_PB; + + if (block_size < SQFS_XZ_MIN_DICT_SIZE) + cfg->opt.xz.dict_size = SQFS_XZ_MIN_DICT_SIZE; + break; + case SQFS_COMP_LZMA: + flag_mask |= SQFS_COMP_FLAG_LZMA_ALL; + cfg->level = SQFS_LZMA_DEFAULT_LEVEL; + cfg->opt.lzma.dict_size = block_size; + cfg->opt.lzma.lc = SQFS_LZMA_DEFAULT_LC; + cfg->opt.lzma.lp = SQFS_LZMA_DEFAULT_LP; + cfg->opt.lzma.pb = SQFS_LZMA_DEFAULT_PB; + + if (block_size < SQFS_LZMA_MIN_DICT_SIZE) + cfg->opt.lzma.dict_size = SQFS_LZMA_MIN_DICT_SIZE; + break; + case SQFS_COMP_LZ4: + flag_mask |= SQFS_COMP_FLAG_LZ4_ALL; + break; + default: + return SQFS_ERROR_UNSUPPORTED; + } + + if (flags & ~flag_mask) { + memset(cfg, 0, sizeof(*cfg)); + return SQFS_ERROR_UNSUPPORTED; + } + + cfg->id = id; + cfg->flags = flags; + cfg->block_size = block_size; + return 0; +} diff --git a/lib/sqfs/src/comp/gzip.c b/lib/sqfs/src/comp/gzip.c new file mode 100644 index 0000000..beacfb8 --- /dev/null +++ b/lib/sqfs/src/comp/gzip.c @@ -0,0 +1,307 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * gzip.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#define SQFS_BUILDING_DLL +#include "config.h" + +#include +#include +#include +#include +#include + +#include "internal.h" + +typedef struct { + sqfs_u32 level; + sqfs_u16 window; + sqfs_u16 strategies; +} gzip_options_t; + +typedef struct { + sqfs_compressor_t base; + + z_stream strm; + bool compress; + + size_t block_size; + gzip_options_t opt; +} gzip_compressor_t; + +static void gzip_destroy(sqfs_object_t *base) +{ + gzip_compressor_t *gzip = (gzip_compressor_t *)base; + + if (gzip->compress) { + deflateEnd(&gzip->strm); + } else { + inflateEnd(&gzip->strm); + } + + free(gzip); +} + +static void gzip_get_configuration(const sqfs_compressor_t *base, + sqfs_compressor_config_t *cfg) +{ + const gzip_compressor_t *gzip = (const gzip_compressor_t *)base; + + memset(cfg, 0, sizeof(*cfg)); + cfg->id = SQFS_COMP_GZIP; + cfg->flags = gzip->opt.strategies; + cfg->block_size = gzip->block_size; + cfg->level = gzip->opt.level; + cfg->opt.gzip.window_size = gzip->opt.window; + + if (!gzip->compress) + cfg->flags |= SQFS_COMP_FLAG_UNCOMPRESS; +} + +static int gzip_write_options(sqfs_compressor_t *base, sqfs_file_t *file) +{ + gzip_compressor_t *gzip = (gzip_compressor_t *)base; + gzip_options_t opt; + + if (gzip->opt.level == SQFS_GZIP_DEFAULT_LEVEL && + gzip->opt.window == SQFS_GZIP_DEFAULT_WINDOW && + gzip->opt.strategies == 0) { + return 0; + } + + opt.level = htole32(gzip->opt.level); + opt.window = htole16(gzip->opt.window); + opt.strategies = htole16(gzip->opt.strategies); + + return sqfs_generic_write_options(file, &opt, sizeof(opt)); +} + +static int gzip_read_options(sqfs_compressor_t *base, sqfs_file_t *file) +{ + gzip_compressor_t *gzip = (gzip_compressor_t *)base; + gzip_options_t opt; + int ret; + + ret = sqfs_generic_read_options(file, &opt, sizeof(opt)); + if (ret) + return ret; + + gzip->opt.level = le32toh(opt.level); + gzip->opt.window = le16toh(opt.window); + gzip->opt.strategies = le16toh(opt.strategies); + + if (gzip->opt.level < 1 || gzip->opt.level > 9) + return SQFS_ERROR_UNSUPPORTED; + + if (gzip->opt.window < 8 || gzip->opt.window > 15) + return SQFS_ERROR_UNSUPPORTED; + + if (gzip->opt.strategies & ~SQFS_COMP_FLAG_GZIP_ALL) + return SQFS_ERROR_UNSUPPORTED; + + return 0; +} + +static int flag_to_zlib_strategy(int flag) +{ + switch (flag) { + case SQFS_COMP_FLAG_GZIP_DEFAULT: + return Z_DEFAULT_STRATEGY; + case SQFS_COMP_FLAG_GZIP_FILTERED: + return Z_FILTERED; + case SQFS_COMP_FLAG_GZIP_HUFFMAN: + return Z_HUFFMAN_ONLY; + case SQFS_COMP_FLAG_GZIP_RLE: + return Z_RLE; + case SQFS_COMP_FLAG_GZIP_FIXED: + return Z_FIXED; + default: + break; + } + + return 0; +} + +static int find_strategy(gzip_compressor_t *gzip, const sqfs_u8 *in, + sqfs_u32 size, sqfs_u8 *out, sqfs_u32 outsize) +{ + int ret, strategy, selected = Z_DEFAULT_STRATEGY; + size_t i, length, minlength = 0; + + for (i = 0x01; i & SQFS_COMP_FLAG_GZIP_ALL; i <<= 1) { + if ((gzip->opt.strategies & i) == 0) + continue; + + ret = deflateReset(&gzip->strm); + if (ret != Z_OK) + return SQFS_ERROR_COMPRESSOR; + + strategy = flag_to_zlib_strategy(i); + + gzip->strm.next_in = (z_const Bytef *)in; + gzip->strm.avail_in = size; + gzip->strm.next_out = out; + gzip->strm.avail_out = outsize; + + ret = deflateParams(&gzip->strm, gzip->opt.level, strategy); + if (ret != Z_OK) + return SQFS_ERROR_COMPRESSOR; + + ret = deflate(&gzip->strm, Z_FINISH); + + if (ret == Z_STREAM_END) { + length = gzip->strm.total_out; + + if (minlength == 0 || length < minlength) { + minlength = length; + selected = strategy; + } + } else if (ret != Z_OK && ret != Z_BUF_ERROR) { + return SQFS_ERROR_COMPRESSOR; + } + } + + return selected; +} + +static sqfs_s32 gzip_do_block(sqfs_compressor_t *base, const sqfs_u8 *in, + sqfs_u32 size, sqfs_u8 *out, sqfs_u32 outsize) +{ + gzip_compressor_t *gzip = (gzip_compressor_t *)base; + int ret, strategy = 0; + size_t written; + + if (size >= 0x7FFFFFFF) + return SQFS_ERROR_ARG_INVALID; + + if (gzip->compress && gzip->opt.strategies != 0) { + strategy = find_strategy(gzip, in, size, out, outsize); + if (strategy < 0) + return strategy; + } + + if (gzip->compress) { + ret = deflateReset(&gzip->strm); + } else { + ret = inflateReset(&gzip->strm); + } + + if (ret != Z_OK) + return SQFS_ERROR_COMPRESSOR; + + gzip->strm.next_in = (const void *)in; + gzip->strm.avail_in = size; + gzip->strm.next_out = out; + gzip->strm.avail_out = outsize; + + if (gzip->compress && gzip->opt.strategies != 0) { + ret = deflateParams(&gzip->strm, gzip->opt.level, strategy); + if (ret != Z_OK) + return SQFS_ERROR_COMPRESSOR; + } + + if (gzip->compress) { + ret = deflate(&gzip->strm, Z_FINISH); + } else { + ret = inflate(&gzip->strm, Z_FINISH); + } + + if (ret == Z_STREAM_END) { + written = gzip->strm.total_out; + + if (gzip->compress && written >= size) + return 0; + + return written; + } + + if (ret != Z_OK && ret != Z_BUF_ERROR) + return SQFS_ERROR_COMPRESSOR; + + return 0; +} + +static sqfs_object_t *gzip_create_copy(const sqfs_object_t *cmp) +{ + gzip_compressor_t *gzip = malloc(sizeof(*gzip)); + int ret; + + if (gzip == NULL) + return NULL; + + memcpy(gzip, cmp, sizeof(*gzip)); + memset(&gzip->strm, 0, sizeof(gzip->strm)); + + if (gzip->compress) { + ret = deflateInit2(&gzip->strm, gzip->opt.level, Z_DEFLATED, + gzip->opt.window, 8, Z_DEFAULT_STRATEGY); + } else { + ret = inflateInit(&gzip->strm); + } + + if (ret != Z_OK) { + free(gzip); + return NULL; + } + + return (sqfs_object_t *)gzip; +} + +int gzip_compressor_create(const sqfs_compressor_config_t *cfg, + sqfs_compressor_t **out) +{ + gzip_compressor_t *gzip; + sqfs_compressor_t *base; + int ret; + + if (cfg->flags & ~(SQFS_COMP_FLAG_GZIP_ALL | + SQFS_COMP_FLAG_GENERIC_ALL)) { + return SQFS_ERROR_UNSUPPORTED; + } + + if (cfg->level < SQFS_GZIP_MIN_LEVEL || + cfg->level > SQFS_GZIP_MAX_LEVEL) { + return SQFS_ERROR_UNSUPPORTED; + } + + if (cfg->opt.gzip.window_size < SQFS_GZIP_MIN_WINDOW || + cfg->opt.gzip.window_size > SQFS_GZIP_MAX_WINDOW) { + return SQFS_ERROR_UNSUPPORTED; + } + + gzip = calloc(1, sizeof(*gzip)); + base = (sqfs_compressor_t *)gzip; + + if (gzip == NULL) + return SQFS_ERROR_ALLOC; + + sqfs_object_init(gzip, gzip_destroy, gzip_create_copy); + + gzip->opt.level = cfg->level; + gzip->opt.window = cfg->opt.gzip.window_size; + gzip->opt.strategies = cfg->flags & SQFS_COMP_FLAG_GZIP_ALL; + gzip->compress = (cfg->flags & SQFS_COMP_FLAG_UNCOMPRESS) == 0; + gzip->block_size = cfg->block_size; + base->get_configuration = gzip_get_configuration; + base->do_block = gzip_do_block; + base->write_options = gzip_write_options; + base->read_options = gzip_read_options; + + if (gzip->compress) { + ret = deflateInit2(&gzip->strm, cfg->level, + Z_DEFLATED, cfg->opt.gzip.window_size, 8, + Z_DEFAULT_STRATEGY); + } else { + ret = inflateInit(&gzip->strm); + } + + if (ret != Z_OK) { + free(gzip); + return SQFS_ERROR_COMPRESSOR; + } + + *out = base; + return 0; +} diff --git a/lib/sqfs/src/comp/internal.h b/lib/sqfs/src/comp/internal.h new file mode 100644 index 0000000..e4c3dd8 --- /dev/null +++ b/lib/sqfs/src/comp/internal.h @@ -0,0 +1,46 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * internal.h + * + * Copyright (C) 2019 David Oberhollenzer + */ +#ifndef INTERNAL_H +#define INTERNAL_H + +#include "config.h" + +#include "sqfs/predef.h" +#include "sqfs/compressor.h" +#include "sqfs/error.h" +#include "sqfs/block.h" +#include "sqfs/io.h" +#include "util/util.h" + +SQFS_INTERNAL +int sqfs_generic_write_options(sqfs_file_t *file, const void *data, + size_t size); + +SQFS_INTERNAL +int sqfs_generic_read_options(sqfs_file_t *file, void *data, size_t size); + +SQFS_INTERNAL +int xz_compressor_create(const sqfs_compressor_config_t *cfg, + sqfs_compressor_t **out); + +SQFS_INTERNAL +int gzip_compressor_create(const sqfs_compressor_config_t *cfg, + sqfs_compressor_t **out); + +SQFS_INTERNAL +int lz4_compressor_create(const sqfs_compressor_config_t *cfg, + sqfs_compressor_t **out); + +SQFS_INTERNAL +int zstd_compressor_create(const sqfs_compressor_config_t *cfg, + sqfs_compressor_t **out); + +SQFS_INTERNAL +int lzma_compressor_create(const sqfs_compressor_config_t *cfg, + sqfs_compressor_t **out); + +#endif /* INTERNAL_H */ diff --git a/lib/sqfs/src/comp/lz4.c b/lib/sqfs/src/comp/lz4.c new file mode 100644 index 0000000..77f4a6e --- /dev/null +++ b/lib/sqfs/src/comp/lz4.c @@ -0,0 +1,172 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * lz4.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#define SQFS_BUILDING_DLL +#include "config.h" + +#include +#include +#include + +#include +#include + +#include "internal.h" + +typedef struct { + sqfs_compressor_t base; + size_t block_size; + bool high_compression; +} lz4_compressor_t; + +typedef struct { + sqfs_u32 version; + sqfs_u32 flags; +} lz4_options; + +#define LZ4LEGACY 1 + +/* old verions of liblz4 don't have this */ +#ifndef LZ4HC_CLEVEL_MAX +#define LZ4HC_CLEVEL_MAX 12 +#endif + +static int lz4_write_options(sqfs_compressor_t *base, sqfs_file_t *file) +{ + lz4_compressor_t *lz4 = (lz4_compressor_t *)base; + lz4_options opt = { + .version = htole32(LZ4LEGACY), + .flags = htole32(lz4->high_compression ? + SQFS_COMP_FLAG_LZ4_HC : 0), + }; + + return sqfs_generic_write_options(file, &opt, sizeof(opt)); +} + +static int lz4_read_options(sqfs_compressor_t *base, sqfs_file_t *file) +{ + lz4_options opt; + int ret; + (void)base; + + ret = sqfs_generic_read_options(file, &opt, sizeof(opt)); + if (ret) + return ret; + + opt.version = le32toh(opt.version); + opt.flags = le32toh(opt.flags); + + if (opt.version != LZ4LEGACY) + return SQFS_ERROR_UNSUPPORTED; + + return 0; +} + +static sqfs_s32 lz4_comp_block(sqfs_compressor_t *base, const sqfs_u8 *in, + sqfs_u32 size, sqfs_u8 *out, sqfs_u32 outsize) +{ + lz4_compressor_t *lz4 = (lz4_compressor_t *)base; + int ret; + + if (size >= 0x7FFFFFFF) + return SQFS_ERROR_ARG_INVALID; + + if (lz4->high_compression) { + ret = LZ4_compress_HC((const void *)in, (void *)out, + size, outsize, LZ4HC_CLEVEL_MAX); + } else { + ret = LZ4_compress_default((const void *)in, (void *)out, + size, outsize); + } + + if (ret < 0) + return SQFS_ERROR_COMPRESSOR; + + return ret; +} + +static sqfs_s32 lz4_uncomp_block(sqfs_compressor_t *base, const sqfs_u8 *in, + sqfs_u32 size, sqfs_u8 *out, sqfs_u32 outsize) +{ + int ret; + (void)base; + + if (outsize >= 0x7FFFFFFF) + return SQFS_ERROR_ARG_INVALID; + + ret = LZ4_decompress_safe((const void *)in, (void *)out, size, outsize); + + if (ret < 0) + return SQFS_ERROR_COMPRESSOR; + + return ret; +} + +static void lz4_get_configuration(const sqfs_compressor_t *base, + sqfs_compressor_config_t *cfg) +{ + const lz4_compressor_t *lz4 = (const lz4_compressor_t *)base; + + memset(cfg, 0, sizeof(*cfg)); + cfg->id = SQFS_COMP_LZ4; + cfg->block_size = lz4->block_size; + + if (lz4->high_compression) + cfg->flags |= SQFS_COMP_FLAG_LZ4_HC; + + if (base->do_block == lz4_uncomp_block) + cfg->flags |= SQFS_COMP_FLAG_UNCOMPRESS; +} + +static sqfs_object_t *lz4_create_copy(const sqfs_object_t *cmp) +{ + lz4_compressor_t *lz4 = malloc(sizeof(*lz4)); + + if (lz4 == NULL) + return NULL; + + memcpy(lz4, cmp, sizeof(*lz4)); + return (sqfs_object_t *)lz4; +} + +static void lz4_destroy(sqfs_object_t *base) +{ + free(base); +} + +int lz4_compressor_create(const sqfs_compressor_config_t *cfg, + sqfs_compressor_t **out) +{ + sqfs_compressor_t *base; + lz4_compressor_t *lz4; + + if (cfg->flags & ~(SQFS_COMP_FLAG_LZ4_ALL | + SQFS_COMP_FLAG_GENERIC_ALL)) { + return SQFS_ERROR_UNSUPPORTED; + } + + if (cfg->level != 0) + return SQFS_ERROR_UNSUPPORTED; + + lz4 = calloc(1, sizeof(*lz4)); + base = (sqfs_compressor_t *)lz4; + if (lz4 == NULL) + return SQFS_ERROR_ALLOC; + + sqfs_object_init(lz4, lz4_destroy, lz4_create_copy); + + lz4->high_compression = (cfg->flags & SQFS_COMP_FLAG_LZ4_HC) != 0; + lz4->block_size = cfg->block_size; + + base->get_configuration = lz4_get_configuration; + base->do_block = (cfg->flags & SQFS_COMP_FLAG_UNCOMPRESS) ? + lz4_uncomp_block : lz4_comp_block; + base->write_options = lz4_write_options; + base->read_options = lz4_read_options; + + *out = base; + return 0; +} diff --git a/lib/sqfs/src/comp/lzma.c b/lib/sqfs/src/comp/lzma.c new file mode 100644 index 0000000..5456603 --- /dev/null +++ b/lib/sqfs/src/comp/lzma.c @@ -0,0 +1,281 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * lzma.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#define SQFS_BUILDING_DLL +#include "config.h" + +#include +#include +#include +#include + +#include "internal.h" + +#define LZMA_SIZE_OFFSET (5) +#define LZMA_SIZE_BYTES (8) +#define LZMA_HEADER_SIZE (13) + +#define MEMLIMIT (64 * 1024 * 1024) + +typedef struct { + sqfs_compressor_t base; + size_t block_size; + size_t dict_size; + + sqfs_u32 flags; + sqfs_u8 level; + sqfs_u8 lc; + sqfs_u8 lp; + sqfs_u8 pb; +} lzma_compressor_t; + +static int lzma_write_options(sqfs_compressor_t *base, sqfs_file_t *file) +{ + (void)base; (void)file; + return 0; +} + +static int lzma_read_options(sqfs_compressor_t *base, sqfs_file_t *file) +{ + (void)base; + (void)file; + return SQFS_ERROR_UNSUPPORTED; +} + +static sqfs_s32 try_compress(lzma_compressor_t *lzma, sqfs_u32 preset, + const sqfs_u8 *in, size_t size, + sqfs_u8 *out, size_t outsize) +{ + lzma_stream strm = LZMA_STREAM_INIT; + lzma_options_lzma opt; + int ret; + + lzma_lzma_preset(&opt, preset); + opt.dict_size = lzma->block_size; + opt.lc = lzma->lc; + opt.lp = lzma->lp; + opt.pb = lzma->pb; + + if (lzma_alone_encoder(&strm, &opt) != LZMA_OK) { + lzma_end(&strm); + return SQFS_ERROR_COMPRESSOR; + } + + strm.next_out = out; + strm.avail_out = outsize; + strm.next_in = in; + strm.avail_in = size; + + ret = lzma_code(&strm, LZMA_FINISH); + lzma_end(&strm); + + if (ret != LZMA_STREAM_END) + return ret == LZMA_OK ? 0 : SQFS_ERROR_COMPRESSOR; + + if (strm.total_out > size) + return 0; + + out[LZMA_SIZE_OFFSET ] = size & 0xFF; + out[LZMA_SIZE_OFFSET + 1] = (size >> 8) & 0xFF; + out[LZMA_SIZE_OFFSET + 2] = (size >> 16) & 0xFF; + out[LZMA_SIZE_OFFSET + 3] = (size >> 24) & 0xFF; + out[LZMA_SIZE_OFFSET + 4] = 0; + out[LZMA_SIZE_OFFSET + 5] = 0; + out[LZMA_SIZE_OFFSET + 6] = 0; + out[LZMA_SIZE_OFFSET + 7] = 0; + return strm.total_out; +} + +static sqfs_s32 lzma_comp_block(sqfs_compressor_t *base, const sqfs_u8 *in, + sqfs_u32 size, sqfs_u8 *out, sqfs_u32 outsize) +{ + lzma_compressor_t *lzma = (lzma_compressor_t *)base; + sqfs_s32 ret, smallest; + sqfs_u32 preset; + + if (outsize < LZMA_HEADER_SIZE || size >= 0x7FFFFFFF) + return SQFS_ERROR_ARG_INVALID; + + preset = lzma->level; + ret = try_compress(lzma, preset, in, size, out, outsize); + if (ret < 0 || !(lzma->flags & SQFS_COMP_FLAG_LZMA_EXTREME)) + return ret; + + preset |= LZMA_PRESET_EXTREME; + smallest = ret; + + ret = try_compress(lzma, preset, in, size, out, outsize); + if (ret < 0 || (ret > 0 && (smallest == 0 || ret < smallest))) + return ret; + + preset &= ~LZMA_PRESET_EXTREME; + return smallest == 0 ? 0 : + try_compress(lzma, preset, in, size, out, outsize); +} + +static sqfs_s32 lzma_uncomp_block(sqfs_compressor_t *base, const sqfs_u8 *in, + sqfs_u32 size, sqfs_u8 *out, sqfs_u32 outsize) +{ + sqfs_u8 lzma_header[LZMA_HEADER_SIZE]; + lzma_stream strm = LZMA_STREAM_INIT; + size_t hdrsize; + int ret; + (void)base; + + if (size >= 0x7FFFFFFF) + return SQFS_ERROR_ARG_INVALID; + + if (size < sizeof(lzma_header)) + return SQFS_ERROR_CORRUPTED; + + hdrsize = (size_t)in[LZMA_SIZE_OFFSET] | + ((size_t)in[LZMA_SIZE_OFFSET + 1] << 8) | + ((size_t)in[LZMA_SIZE_OFFSET + 2] << 16) | + ((size_t)in[LZMA_SIZE_OFFSET + 3] << 24); + + if (hdrsize > outsize) + return 0; + + if (lzma_alone_decoder(&strm, MEMLIMIT) != LZMA_OK) { + lzma_end(&strm); + return SQFS_ERROR_COMPRESSOR; + } + + memcpy(lzma_header, in, sizeof(lzma_header)); + memset(lzma_header + LZMA_SIZE_OFFSET, 0xFF, LZMA_SIZE_BYTES); + + strm.next_out = out; + strm.avail_out = outsize; + strm.next_in = lzma_header; + strm.avail_in = sizeof(lzma_header); + + ret = lzma_code(&strm, LZMA_RUN); + + if (ret != LZMA_OK || strm.avail_in != 0) { + lzma_end(&strm); + return SQFS_ERROR_COMPRESSOR; + } + + strm.next_in = in + sizeof(lzma_header); + strm.avail_in = size - sizeof(lzma_header); + + ret = lzma_code(&strm, LZMA_FINISH); + lzma_end(&strm); + + if (ret != LZMA_STREAM_END && ret != LZMA_OK) + return SQFS_ERROR_COMPRESSOR; + + if (ret == LZMA_OK) { + if (strm.total_out < hdrsize || strm.avail_in != 0) + return 0; + } + + return hdrsize; +} + +static void lzma_get_configuration(const sqfs_compressor_t *base, + sqfs_compressor_config_t *cfg) +{ + const lzma_compressor_t *lzma = (const lzma_compressor_t *)base; + + memset(cfg, 0, sizeof(*cfg)); + cfg->id = SQFS_COMP_LZMA; + cfg->block_size = lzma->block_size; + cfg->flags = lzma->flags; + cfg->level = lzma->level; + cfg->opt.lzma.dict_size = lzma->dict_size; + cfg->opt.lzma.lc = lzma->lc; + cfg->opt.lzma.lp = lzma->lp; + cfg->opt.lzma.pb = lzma->pb; +} + +static sqfs_object_t *lzma_create_copy(const sqfs_object_t *cmp) +{ + lzma_compressor_t *copy = malloc(sizeof(*copy)); + + if (copy != NULL) + memcpy(copy, cmp, sizeof(*copy)); + + return (sqfs_object_t *)copy; +} + +static void lzma_destroy(sqfs_object_t *base) +{ + free(base); +} + +int lzma_compressor_create(const sqfs_compressor_config_t *cfg, + sqfs_compressor_t **out) +{ + sqfs_compressor_t *base; + lzma_compressor_t *lzma; + sqfs_u32 mask; + + mask = SQFS_COMP_FLAG_GENERIC_ALL | SQFS_COMP_FLAG_LZMA_ALL; + + if (cfg->flags & ~mask) + return SQFS_ERROR_UNSUPPORTED; + + /* XXX: values are unsigned and minimum is 0 */ + if (cfg->level > SQFS_LZMA_MAX_LEVEL) + return SQFS_ERROR_UNSUPPORTED; + + if (cfg->opt.lzma.lc > SQFS_LZMA_MAX_LC) + return SQFS_ERROR_UNSUPPORTED; + + if (cfg->opt.lzma.lp > SQFS_LZMA_MAX_LP) + return SQFS_ERROR_UNSUPPORTED; + + if (cfg->opt.lzma.pb > SQFS_LZMA_MAX_PB) + return SQFS_ERROR_UNSUPPORTED; + + if (cfg->opt.lzma.lc + cfg->opt.lzma.lp > 4) + return SQFS_ERROR_UNSUPPORTED; + + if (cfg->opt.lzma.dict_size == 0) + return SQFS_ERROR_UNSUPPORTED; + + if (cfg->opt.lzma.dict_size < SQFS_LZMA_MIN_DICT_SIZE) + return SQFS_ERROR_UNSUPPORTED; + + if (cfg->opt.lzma.dict_size > SQFS_LZMA_MAX_DICT_SIZE) + return SQFS_ERROR_UNSUPPORTED; + + mask = cfg->opt.lzma.dict_size; + mask &= mask - 1; + + if (mask != 0) { + if ((mask & (mask - 1)) != 0) + return SQFS_ERROR_UNSUPPORTED; + + if (cfg->opt.lzma.dict_size != (mask | mask >> 1)) + return SQFS_ERROR_UNSUPPORTED; + } + + lzma = calloc(1, sizeof(*lzma)); + base = (sqfs_compressor_t *)lzma; + if (lzma == NULL) + return SQFS_ERROR_ALLOC; + + sqfs_object_init(lzma, lzma_destroy, lzma_create_copy); + + lzma->block_size = cfg->block_size; + lzma->flags = cfg->flags; + lzma->level = cfg->level; + lzma->dict_size = cfg->opt.lzma.dict_size; + lzma->lc = cfg->opt.lzma.lc; + lzma->lp = cfg->opt.lzma.lp; + lzma->pb = cfg->opt.lzma.pb; + + base->get_configuration = lzma_get_configuration; + base->do_block = (cfg->flags & SQFS_COMP_FLAG_UNCOMPRESS) ? + lzma_uncomp_block : lzma_comp_block; + base->write_options = lzma_write_options; + base->read_options = lzma_read_options; + + *out = base; + return 0; +} diff --git a/lib/sqfs/src/comp/xz.c b/lib/sqfs/src/comp/xz.c new file mode 100644 index 0000000..13545ed --- /dev/null +++ b/lib/sqfs/src/comp/xz.c @@ -0,0 +1,324 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * xz.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#define SQFS_BUILDING_DLL +#include "config.h" + +#include +#include +#include +#include + +#include "internal.h" + +typedef struct { + sqfs_compressor_t base; + size_t block_size; + size_t dict_size; + + sqfs_u8 level; + sqfs_u8 lc; + sqfs_u8 lp; + sqfs_u8 pb; + + int flags; +} xz_compressor_t; + +typedef struct { + sqfs_u32 dict_size; + sqfs_u32 flags; +} xz_options_t; + +static bool is_dict_size_valid(size_t size) +{ + size_t x = size & (size - 1); + + if (x == 0) + return true; + + return size == (x | (x >> 1)); +} + +static int xz_write_options(sqfs_compressor_t *base, sqfs_file_t *file) +{ + xz_compressor_t *xz = (xz_compressor_t *)base; + xz_options_t opt; + sqfs_u32 flags; + + if (xz->flags == 0 && xz->dict_size == xz->block_size) + return 0; + + flags = xz->flags & SQFS_COMP_FLAG_XZ_ALL; + flags &= ~SQFS_COMP_FLAG_XZ_EXTREME; + + opt.dict_size = htole32(xz->dict_size); + opt.flags = htole32(flags); + + return sqfs_generic_write_options(file, &opt, sizeof(opt)); +} + +static int xz_read_options(sqfs_compressor_t *base, sqfs_file_t *file) +{ + xz_compressor_t *xz = (xz_compressor_t *)base; + xz_options_t opt; + int ret; + + ret = sqfs_generic_read_options(file, &opt, sizeof(opt)); + if (ret) + return ret; + + opt.dict_size = le32toh(opt.dict_size); + opt.flags = le32toh(opt.flags); + + if (!is_dict_size_valid(opt.dict_size)) + return SQFS_ERROR_CORRUPTED; + + if (opt.flags & ~SQFS_COMP_FLAG_XZ_ALL) + return SQFS_ERROR_UNSUPPORTED; + + xz->flags = opt.flags; + xz->dict_size = opt.dict_size; + return 0; +} + +static sqfs_s32 compress(xz_compressor_t *xz, lzma_vli filter, + const sqfs_u8 *in, sqfs_u32 size, + sqfs_u8 *out, sqfs_u32 outsize, + sqfs_u32 presets) +{ + lzma_filter filters[5]; + lzma_options_lzma opt; + size_t written = 0; + lzma_ret ret; + int i = 0; + + if (lzma_lzma_preset(&opt, presets)) + return SQFS_ERROR_COMPRESSOR; + + opt.lc = xz->lc; + opt.lp = xz->lp; + opt.pb = xz->pb; + opt.dict_size = xz->dict_size; + + if (filter != LZMA_VLI_UNKNOWN) { + filters[i].id = filter; + filters[i].options = NULL; + ++i; + } + + filters[i].id = LZMA_FILTER_LZMA2; + filters[i].options = &opt; + ++i; + + filters[i].id = LZMA_VLI_UNKNOWN; + filters[i].options = NULL; + ++i; + + ret = lzma_stream_buffer_encode(filters, LZMA_CHECK_CRC32, NULL, + in, size, out, &written, outsize); + + if (ret == LZMA_OK) + return (written >= size) ? 0 : written; + + if (ret != LZMA_BUF_ERROR) + return SQFS_ERROR_COMPRESSOR; + + return 0; +} + +static lzma_vli flag_to_vli(int flag) +{ + switch (flag) { + case SQFS_COMP_FLAG_XZ_X86: + return LZMA_FILTER_X86; + case SQFS_COMP_FLAG_XZ_POWERPC: + return LZMA_FILTER_POWERPC; + case SQFS_COMP_FLAG_XZ_IA64: + return LZMA_FILTER_IA64; + case SQFS_COMP_FLAG_XZ_ARM: + return LZMA_FILTER_ARM; + case SQFS_COMP_FLAG_XZ_ARMTHUMB: + return LZMA_FILTER_ARMTHUMB; + case SQFS_COMP_FLAG_XZ_SPARC: + return LZMA_FILTER_SPARC; + default: + break; + } + + return LZMA_VLI_UNKNOWN; +} + +static sqfs_s32 xz_comp_block(sqfs_compressor_t *base, const sqfs_u8 *in, + sqfs_u32 size, sqfs_u8 *out, sqfs_u32 outsize) +{ + xz_compressor_t *xz = (xz_compressor_t *)base; + lzma_vli filter, selected = LZMA_VLI_UNKNOWN; + sqfs_s32 ret, smallest; + bool extreme; + size_t i; + + if (size >= 0x7FFFFFFF) + return SQFS_ERROR_ARG_INVALID; + + ret = compress(xz, LZMA_VLI_UNKNOWN, in, size, out, + outsize, xz->level); + if (ret < 0 || xz->flags == 0) + return ret; + + smallest = ret; + extreme = false; + + if (xz->flags & SQFS_COMP_FLAG_XZ_EXTREME) { + ret = compress(xz, LZMA_VLI_UNKNOWN, in, size, out, outsize, + xz->level | LZMA_PRESET_EXTREME); + + if (ret > 0 && (smallest == 0 || ret < smallest)) { + smallest = ret; + extreme = true; + } + } + + for (i = 1; i & SQFS_COMP_FLAG_XZ_ALL; i <<= 1) { + if ((i & SQFS_COMP_FLAG_XZ_EXTREME) || (xz->flags & i) == 0) + continue; + + filter = flag_to_vli(i); + + ret = compress(xz, filter, in, size, out, outsize, xz->level); + if (ret > 0 && (smallest == 0 || ret < smallest)) { + smallest = ret; + selected = filter; + extreme = false; + } + + if (xz->flags & SQFS_COMP_FLAG_XZ_EXTREME) { + ret = compress(xz, filter, in, size, out, outsize, + xz->level | LZMA_PRESET_EXTREME); + + if (ret > 0 && (smallest == 0 || ret < smallest)) { + smallest = ret; + selected = filter; + extreme = true; + } + } + } + + if (smallest == 0) + return 0; + + return compress(xz, selected, in, size, out, outsize, + xz->level | (extreme ? LZMA_PRESET_EXTREME : 0)); +} + +static sqfs_s32 xz_uncomp_block(sqfs_compressor_t *base, const sqfs_u8 *in, + sqfs_u32 size, sqfs_u8 *out, sqfs_u32 outsize) +{ + sqfs_u64 memlimit = 65 * 1024 * 1024; + size_t dest_pos = 0; + size_t src_pos = 0; + lzma_ret ret; + (void)base; + + if (outsize >= 0x7FFFFFFF) + return SQFS_ERROR_ARG_INVALID; + + ret = lzma_stream_buffer_decode(&memlimit, 0, NULL, + in, &src_pos, size, + out, &dest_pos, outsize); + + if (ret == LZMA_OK && size == src_pos) + return dest_pos; + + return SQFS_ERROR_COMPRESSOR; +} + +static void xz_get_configuration(const sqfs_compressor_t *base, + sqfs_compressor_config_t *cfg) +{ + const xz_compressor_t *xz = (const xz_compressor_t *)base; + + memset(cfg, 0, sizeof(*cfg)); + cfg->id = SQFS_COMP_XZ; + cfg->flags = xz->flags; + cfg->block_size = xz->block_size; + cfg->level = xz->level; + cfg->opt.xz.dict_size = xz->dict_size; + cfg->opt.xz.lc = xz->lc; + cfg->opt.xz.lp = xz->lp; + cfg->opt.xz.pb = xz->pb; + + if (base->do_block == xz_uncomp_block) + cfg->flags |= SQFS_COMP_FLAG_UNCOMPRESS; +} + +static sqfs_object_t *xz_create_copy(const sqfs_object_t *cmp) +{ + xz_compressor_t *xz = malloc(sizeof(*xz)); + + if (xz == NULL) + return NULL; + + memcpy(xz, cmp, sizeof(*xz)); + return (sqfs_object_t *)xz; +} + +static void xz_destroy(sqfs_object_t *base) +{ + free(base); +} + +int xz_compressor_create(const sqfs_compressor_config_t *cfg, + sqfs_compressor_t **out) +{ + sqfs_compressor_t *base; + xz_compressor_t *xz; + + if (cfg->flags & ~(SQFS_COMP_FLAG_GENERIC_ALL | + SQFS_COMP_FLAG_XZ_ALL)) { + return SQFS_ERROR_UNSUPPORTED; + } + + if (!is_dict_size_valid(cfg->opt.xz.dict_size)) + return SQFS_ERROR_UNSUPPORTED; + + if (cfg->opt.xz.lc + cfg->opt.xz.lp > 4) + return SQFS_ERROR_UNSUPPORTED; + + if (cfg->opt.xz.pb > SQFS_XZ_MAX_PB) + return SQFS_ERROR_UNSUPPORTED; + + if (cfg->level > SQFS_XZ_MAX_LEVEL) + return SQFS_ERROR_UNSUPPORTED; + + if (cfg->opt.xz.dict_size < SQFS_XZ_MIN_DICT_SIZE) + return SQFS_ERROR_UNSUPPORTED; + + if (cfg->opt.xz.dict_size > SQFS_XZ_MAX_DICT_SIZE) + return SQFS_ERROR_UNSUPPORTED; + + xz = calloc(1, sizeof(*xz)); + base = (sqfs_compressor_t *)xz; + if (xz == NULL) + return SQFS_ERROR_ALLOC; + + sqfs_object_init(xz, xz_destroy, xz_create_copy); + + xz->flags = cfg->flags; + xz->dict_size = cfg->opt.xz.dict_size; + xz->block_size = cfg->block_size; + xz->lc = cfg->opt.xz.lc; + xz->lp = cfg->opt.xz.lp; + xz->pb = cfg->opt.xz.pb; + xz->level = cfg->level; + base->get_configuration = xz_get_configuration; + base->do_block = (cfg->flags & SQFS_COMP_FLAG_UNCOMPRESS) ? + xz_uncomp_block : xz_comp_block; + base->write_options = xz_write_options; + base->read_options = xz_read_options; + + *out = base; + return 0; +} diff --git a/lib/sqfs/src/comp/zstd.c b/lib/sqfs/src/comp/zstd.c new file mode 100644 index 0000000..a6d7975 --- /dev/null +++ b/lib/sqfs/src/comp/zstd.c @@ -0,0 +1,172 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * zstd.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#define SQFS_BUILDING_DLL +#include "config.h" + +#include +#include +#include + +#include +#include + +#include "internal.h" + +typedef struct { + sqfs_compressor_t base; + size_t block_size; + ZSTD_CCtx *zctx; + int level; +} zstd_compressor_t; + +typedef struct { + sqfs_u32 level; +} zstd_options_t; + +static int zstd_write_options(sqfs_compressor_t *base, sqfs_file_t *file) +{ + zstd_compressor_t *zstd = (zstd_compressor_t *)base; + zstd_options_t opt; + + if (zstd->level == SQFS_ZSTD_DEFAULT_LEVEL) + return 0; + + opt.level = htole32(zstd->level); + return sqfs_generic_write_options(file, &opt, sizeof(opt)); +} + +static int zstd_read_options(sqfs_compressor_t *base, sqfs_file_t *file) +{ + zstd_options_t opt; + int ret; + (void)base; + + ret = sqfs_generic_read_options(file, &opt, sizeof(opt)); + if (ret) + return ret; + + opt.level = le32toh(opt.level); + return 0; +} + +static sqfs_s32 zstd_comp_block(sqfs_compressor_t *base, const sqfs_u8 *in, + sqfs_u32 size, sqfs_u8 *out, sqfs_u32 outsize) +{ + zstd_compressor_t *zstd = (zstd_compressor_t *)base; + size_t ret; + + if (size >= 0x7FFFFFFF) + return SQFS_ERROR_ARG_INVALID; + + ret = ZSTD_compressCCtx(zstd->zctx, out, outsize, in, size, + zstd->level); + + if (ZSTD_isError(ret)) { + if (ZSTD_getErrorCode(ret) == ZSTD_error_dstSize_tooSmall) + return 0; + + return SQFS_ERROR_COMPRESSOR; + } + + return ret < size ? ret : 0; +} + +static sqfs_s32 zstd_uncomp_block(sqfs_compressor_t *base, const sqfs_u8 *in, + sqfs_u32 size, sqfs_u8 *out, sqfs_u32 outsize) +{ + size_t ret; + (void)base; + + if (outsize >= 0x7FFFFFFF) + return SQFS_ERROR_ARG_INVALID; + + ret = ZSTD_decompress(out, outsize, in, size); + + if (ZSTD_isError(ret)) + return SQFS_ERROR_COMPRESSOR; + + return ret; +} + +static void zstd_get_configuration(const sqfs_compressor_t *base, + sqfs_compressor_config_t *cfg) +{ + const zstd_compressor_t *zstd = (const zstd_compressor_t *)base; + + memset(cfg, 0, sizeof(*cfg)); + cfg->id = SQFS_COMP_ZSTD; + + cfg->block_size = zstd->block_size; + cfg->level = zstd->level; + + if (base->do_block == zstd_uncomp_block) + cfg->flags |= SQFS_COMP_FLAG_UNCOMPRESS; +} + +static sqfs_object_t *zstd_create_copy(const sqfs_object_t *cmp) +{ + zstd_compressor_t *zstd = malloc(sizeof(*zstd)); + + if (zstd == NULL) + return NULL; + + memcpy(zstd, cmp, sizeof(*zstd)); + + zstd->zctx = ZSTD_createCCtx(); + + if (zstd->zctx == NULL) { + free(zstd); + return NULL; + } + + return (sqfs_object_t *)zstd; +} + +static void zstd_destroy(sqfs_object_t *base) +{ + zstd_compressor_t *zstd = (zstd_compressor_t *)base; + + ZSTD_freeCCtx(zstd->zctx); + free(zstd); +} + +int zstd_compressor_create(const sqfs_compressor_config_t *cfg, + sqfs_compressor_t **out) +{ + zstd_compressor_t *zstd; + sqfs_compressor_t *base; + + if (cfg->flags & ~SQFS_COMP_FLAG_GENERIC_ALL) + return SQFS_ERROR_UNSUPPORTED; + + if (cfg->level < 1 || cfg->level > (unsigned int)ZSTD_maxCLevel()) + return SQFS_ERROR_UNSUPPORTED; + + zstd = calloc(1, sizeof(*zstd)); + base = (sqfs_compressor_t *)zstd; + if (zstd == NULL) + return SQFS_ERROR_ALLOC; + + sqfs_object_init(zstd, zstd_destroy, zstd_create_copy); + + zstd->block_size = cfg->block_size; + zstd->level = cfg->level; + zstd->zctx = ZSTD_createCCtx(); + if (zstd->zctx == NULL) { + free(zstd); + return SQFS_ERROR_COMPRESSOR; + } + + base->get_configuration = zstd_get_configuration; + base->do_block = cfg->flags & SQFS_COMP_FLAG_UNCOMPRESS ? + zstd_uncomp_block : zstd_comp_block; + base->write_options = zstd_write_options; + base->read_options = zstd_read_options; + + *out = base; + return 0; +} diff --git a/lib/sqfs/src/data_reader.c b/lib/sqfs/src/data_reader.c new file mode 100644 index 0000000..3f0cd74 --- /dev/null +++ b/lib/sqfs/src/data_reader.c @@ -0,0 +1,374 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * data_reader.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#define SQFS_BUILDING_DLL +#include "config.h" + +#include "sqfs/data_reader.h" +#include "sqfs/compressor.h" +#include "sqfs/frag_table.h" +#include "sqfs/block.h" +#include "sqfs/error.h" +#include "sqfs/table.h" +#include "sqfs/inode.h" +#include "sqfs/io.h" +#include "util/util.h" + +#include +#include + +struct sqfs_data_reader_t { + sqfs_object_t obj; + + sqfs_frag_table_t *frag_tbl; + sqfs_compressor_t *cmp; + sqfs_file_t *file; + + sqfs_u8 *data_block; + size_t data_blk_size; + sqfs_u64 current_block; + + sqfs_u8 *frag_block; + size_t frag_blk_size; + sqfs_u32 current_frag_index; + sqfs_u32 block_size; + + sqfs_u8 scratch[]; +}; + +static int get_block(sqfs_data_reader_t *data, sqfs_u64 off, sqfs_u32 size, + sqfs_u32 max_size, size_t *out_sz, sqfs_u8 **out) +{ + sqfs_u32 on_disk_size; + sqfs_s32 ret; + int err; + + *out = alloc_array(1, max_size); + *out_sz = max_size; + + if (*out == NULL) { + err = SQFS_ERROR_ALLOC; + goto fail; + } + + if (SQFS_IS_SPARSE_BLOCK(size)) + return 0; + + on_disk_size = SQFS_ON_DISK_BLOCK_SIZE(size); + + if (on_disk_size > max_size) { + err = SQFS_ERROR_OVERFLOW; + goto fail; + } + + if (SQFS_IS_BLOCK_COMPRESSED(size)) { + err = data->file->read_at(data->file, off, + data->scratch, on_disk_size); + if (err) + goto fail; + + ret = data->cmp->do_block(data->cmp, data->scratch, + on_disk_size, *out, max_size); + if (ret <= 0) { + err = ret < 0 ? ret : SQFS_ERROR_OVERFLOW; + goto fail; + } + + *out_sz = ret; + } else { + err = data->file->read_at(data->file, off, + *out, on_disk_size); + if (err) + goto fail; + + *out_sz = on_disk_size; + } + + return 0; +fail: + free(*out); + *out = NULL; + *out_sz = 0; + return err; +} + +static int precache_data_block(sqfs_data_reader_t *data, sqfs_u64 location, + sqfs_u32 size) +{ + if (data->data_block != NULL && data->current_block == location) + return 0; + + free(data->data_block); + data->current_block = location; + + return get_block(data, location, size, data->block_size, + &data->data_blk_size, &data->data_block); +} + +static int precache_fragment_block(sqfs_data_reader_t *data, size_t idx) +{ + sqfs_fragment_t ent; + int ret; + + if (data->frag_block != NULL && idx == data->current_frag_index) + return 0; + + ret = sqfs_frag_table_lookup(data->frag_tbl, idx, &ent); + if (ret != 0) + return ret; + + free(data->frag_block); + data->current_frag_index = idx; + + return get_block(data, ent.start_offset, ent.size, data->block_size, + &data->frag_blk_size, &data->frag_block); +} + +static void data_reader_destroy(sqfs_object_t *obj) +{ + sqfs_data_reader_t *data = (sqfs_data_reader_t *)obj; + + sqfs_drop(data->cmp); + sqfs_drop(data->file); + sqfs_drop(data->frag_tbl); + free(data->data_block); + free(data->frag_block); + free(data); +} + +static sqfs_object_t *data_reader_copy(const sqfs_object_t *obj) +{ + const sqfs_data_reader_t *data = (const sqfs_data_reader_t *)obj; + sqfs_data_reader_t *copy; + + copy = alloc_flex(sizeof(*data), 1, data->block_size); + if (copy == NULL) + return NULL; + + memcpy(copy, data, sizeof(*data) + data->block_size); + + copy->frag_tbl = sqfs_copy(data->frag_tbl); + if (copy->frag_tbl == NULL) + goto fail_ftbl; + + if (data->data_block != NULL) { + copy->data_block = malloc(data->data_blk_size); + if (copy->data_block == NULL) + goto fail_dblk; + + memcpy(copy->data_block, data->data_block, + data->data_blk_size); + } + + if (copy->frag_block != NULL) { + copy->frag_block = malloc(copy->frag_blk_size); + if (copy->frag_block == NULL) + goto fail_fblk; + + memcpy(copy->frag_block, data->frag_block, + data->frag_blk_size); + } + + /* duplicate references */ + copy->file = sqfs_grab(copy->file); + copy->cmp = sqfs_grab(copy->cmp); + return (sqfs_object_t *)copy; +fail_fblk: + free(copy->data_block); +fail_dblk: + sqfs_drop(copy->frag_tbl); +fail_ftbl: + free(copy); + return NULL; +} + +sqfs_data_reader_t *sqfs_data_reader_create(sqfs_file_t *file, + size_t block_size, + sqfs_compressor_t *cmp, + sqfs_u32 flags) +{ + sqfs_data_reader_t *data; + + if (flags != 0) + return NULL; + + data = alloc_flex(sizeof(*data), 1, block_size); + if (data == NULL) + return NULL; + + sqfs_object_init(data, data_reader_destroy, data_reader_copy); + + data->frag_tbl = sqfs_frag_table_create(0); + if (data->frag_tbl == NULL) { + free(data); + return NULL; + } + + data->file = sqfs_grab(file); + data->block_size = block_size; + data->cmp = sqfs_grab(cmp); + return data; +} + +int sqfs_data_reader_load_fragment_table(sqfs_data_reader_t *data, + const sqfs_super_t *super) +{ + int ret; + + free(data->frag_block); + data->frag_block = NULL; + data->current_frag_index = 0; + + ret = sqfs_frag_table_read(data->frag_tbl, data->file, + super, data->cmp); + if (ret != 0) + return ret; + + data->current_frag_index = sqfs_frag_table_get_size(data->frag_tbl); + return 0; +} + +int sqfs_data_reader_get_block(sqfs_data_reader_t *data, + const sqfs_inode_generic_t *inode, + size_t index, size_t *size, sqfs_u8 **out) +{ + size_t i, unpacked_size; + sqfs_u64 off, filesz; + + sqfs_inode_get_file_block_start(inode, &off); + sqfs_inode_get_file_size(inode, &filesz); + + if (index >= sqfs_inode_get_file_block_count(inode)) + return SQFS_ERROR_OUT_OF_BOUNDS; + + for (i = 0; i < index; ++i) { + off += SQFS_ON_DISK_BLOCK_SIZE(inode->extra[i]); + filesz -= data->block_size; + } + + unpacked_size = filesz < data->block_size ? filesz : data->block_size; + + return get_block(data, off, inode->extra[index], + unpacked_size, size, out); +} + +int sqfs_data_reader_get_fragment(sqfs_data_reader_t *data, + const sqfs_inode_generic_t *inode, + size_t *size, sqfs_u8 **out) +{ + sqfs_u32 frag_idx, frag_off, frag_sz; + size_t block_count; + sqfs_u64 filesz; + int err; + + sqfs_inode_get_file_size(inode, &filesz); + sqfs_inode_get_frag_location(inode, &frag_idx, &frag_off); + *size = 0; + *out = NULL; + + block_count = sqfs_inode_get_file_block_count(inode); + + if (block_count > (UINT64_MAX / data->block_size)) + return SQFS_ERROR_OVERFLOW; + + if ((sqfs_u64)block_count * data->block_size >= filesz) + return 0; + + frag_sz = filesz % data->block_size; + + err = precache_fragment_block(data, frag_idx); + if (err) + return err; + + if (frag_off + frag_sz > data->block_size) + return SQFS_ERROR_OUT_OF_BOUNDS; + + *out = alloc_array(1, frag_sz); + if (*out == NULL) + return SQFS_ERROR_ALLOC; + + *size = frag_sz; + memcpy(*out, (char *)data->frag_block + frag_off, frag_sz); + return 0; +} + +sqfs_s32 sqfs_data_reader_read(sqfs_data_reader_t *data, + const sqfs_inode_generic_t *inode, + sqfs_u64 offset, void *buffer, sqfs_u32 size) +{ + sqfs_u32 frag_idx, frag_off, diff, total = 0; + size_t i, block_count; + sqfs_u64 off, filesz; + char *ptr; + int err; + + if (size >= 0x7FFFFFFF) + size = 0x7FFFFFFE; + + /* work out file location and size */ + sqfs_inode_get_file_size(inode, &filesz); + sqfs_inode_get_frag_location(inode, &frag_idx, &frag_off); + sqfs_inode_get_file_block_start(inode, &off); + block_count = sqfs_inode_get_file_block_count(inode); + + if (offset >= filesz) + return 0; + + if ((filesz - offset) < (sqfs_u64)size) + size = filesz - offset; + + if (size == 0) + return 0; + + /* find location of the first block */ + for (i = 0; offset > data->block_size && i < block_count; ++i) { + off += SQFS_ON_DISK_BLOCK_SIZE(inode->extra[i]); + offset -= data->block_size; + } + + /* copy data from blocks */ + while (i < block_count && size > 0) { + diff = data->block_size - offset; + if (size < diff) + diff = size; + + if (SQFS_IS_SPARSE_BLOCK(inode->extra[i])) { + memset(buffer, 0, diff); + } else { + err = precache_data_block(data, off, inode->extra[i]); + if (err) + return err; + + memcpy(buffer, (char *)data->data_block + offset, diff); + off += SQFS_ON_DISK_BLOCK_SIZE(inode->extra[i]); + } + + ++i; + offset = 0; + size -= diff; + total += diff; + buffer = (char *)buffer + diff; + } + + /* copy from fragment */ + if (size > 0) { + err = precache_fragment_block(data, frag_idx); + if (err) + return err; + + if ((frag_off + offset) >= data->frag_blk_size) + return SQFS_ERROR_OUT_OF_BOUNDS; + + if ((data->frag_blk_size - (frag_off + offset)) < size) + return SQFS_ERROR_OUT_OF_BOUNDS; + + ptr = (char *)data->frag_block + frag_off + offset; + memcpy(buffer, ptr, size); + total += size; + } + + return total; +} diff --git a/lib/sqfs/src/dir_reader/dir_reader.c b/lib/sqfs/src/dir_reader/dir_reader.c new file mode 100644 index 0000000..d70f729 --- /dev/null +++ b/lib/sqfs/src/dir_reader/dir_reader.c @@ -0,0 +1,366 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * fs_reader.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#define SQFS_BUILDING_DLL +#include "internal.h" + +static int inode_copy(const sqfs_inode_generic_t *inode, + sqfs_inode_generic_t **out) +{ + *out = alloc_flex(sizeof(*inode), 1, inode->payload_bytes_used); + if (*out == NULL) + return SQFS_ERROR_ALLOC; + + memcpy(*out, inode, sizeof(*inode) + inode->payload_bytes_used); + return 0; +} + +static int dcache_key_compare(const void *ctx, const void *l, const void *r) +{ + sqfs_u32 lhs = *((const sqfs_u32 *)l), rhs = *((const sqfs_u32 *)r); + (void)ctx; + + return lhs < rhs ? -1 : (lhs > rhs ? 1 : 0); +} + +static int dcache_add(sqfs_dir_reader_t *rd, + const sqfs_inode_generic_t *inode, sqfs_u64 ref) +{ + sqfs_u32 inum = inode->base.inode_number; + + if (!(rd->flags & SQFS_DIR_READER_DOT_ENTRIES)) + return 0; + + if (inode->base.type != SQFS_INODE_DIR && + inode->base.type != SQFS_INODE_EXT_DIR) { + return 0; + } + + if (rbtree_lookup(&rd->dcache, &inum) != NULL) + return 0; + + return rbtree_insert(&rd->dcache, &inum, &ref); +} + +static int dcache_find(sqfs_dir_reader_t *rd, sqfs_u32 inode, sqfs_u64 *ref) +{ + rbtree_node_t *node; + + if (!(rd->flags & SQFS_DIR_READER_DOT_ENTRIES)) + return SQFS_ERROR_NO_ENTRY; + + node = rbtree_lookup(&rd->dcache, &inode); + if (node == NULL) + return SQFS_ERROR_NO_ENTRY; + + *ref = *((sqfs_u64 *)rbtree_node_value(node)); + return 0; +} + +static void dir_reader_destroy(sqfs_object_t *obj) +{ + sqfs_dir_reader_t *rd = (sqfs_dir_reader_t *)obj; + + if (rd->flags & SQFS_DIR_READER_DOT_ENTRIES) + rbtree_cleanup(&rd->dcache); + + sqfs_drop(rd->meta_inode); + sqfs_drop(rd->meta_dir); + free(rd); +} + +static sqfs_object_t *dir_reader_copy(const sqfs_object_t *obj) +{ + const sqfs_dir_reader_t *rd = (const sqfs_dir_reader_t *)obj; + sqfs_dir_reader_t *copy = malloc(sizeof(*copy)); + + if (copy == NULL) + return NULL; + + memcpy(copy, rd, sizeof(*copy)); + + if (rd->flags & SQFS_DIR_READER_DOT_ENTRIES) { + if (rbtree_copy(&rd->dcache, ©->dcache)) + goto fail_cache; + } + + copy->meta_inode = sqfs_copy(rd->meta_inode); + if (copy->meta_inode == NULL) + goto fail_mino; + + copy->meta_dir = sqfs_copy(rd->meta_dir); + if (copy->meta_dir == NULL) + goto fail_mdir; + + return (sqfs_object_t *)copy; +fail_mdir: + sqfs_drop(copy->meta_inode); +fail_mino: + if (copy->flags & SQFS_DIR_READER_DOT_ENTRIES) + rbtree_cleanup(©->dcache); +fail_cache: + free(copy); + return NULL; +} + +sqfs_dir_reader_t *sqfs_dir_reader_create(const sqfs_super_t *super, + sqfs_compressor_t *cmp, + sqfs_file_t *file, + sqfs_u32 flags) +{ + sqfs_dir_reader_t *rd; + sqfs_u64 start, limit; + int ret; + + if (flags & ~SQFS_DIR_READER_ALL_FLAGS) + return NULL; + + rd = calloc(1, sizeof(*rd)); + if (rd == NULL) + return NULL; + + sqfs_object_init(rd, dir_reader_destroy, dir_reader_copy); + + if (flags & SQFS_DIR_READER_DOT_ENTRIES) { + ret = rbtree_init(&rd->dcache, sizeof(sqfs_u32), + sizeof(sqfs_u64), dcache_key_compare); + + if (ret != 0) + goto fail_dcache; + } + + start = super->inode_table_start; + limit = super->directory_table_start; + + rd->meta_inode = sqfs_meta_reader_create(file, cmp, start, limit); + if (rd->meta_inode == NULL) + goto fail_mino; + + start = super->directory_table_start; + limit = super->id_table_start; + + if (super->fragment_table_start < limit) + limit = super->fragment_table_start; + + if (super->export_table_start < limit) + limit = super->export_table_start; + + rd->meta_dir = sqfs_meta_reader_create(file, cmp, start, limit); + if (rd->meta_dir == NULL) + goto fail_mdir; + + rd->super = *super; + rd->flags = flags; + rd->state = DIR_STATE_NONE; + return rd; +fail_mdir: + sqfs_drop(rd->meta_inode); +fail_mino: + if (flags & SQFS_DIR_READER_DOT_ENTRIES) + rbtree_cleanup(&rd->dcache); +fail_dcache: + free(rd); + return NULL; +} + +int sqfs_dir_reader_open_dir(sqfs_dir_reader_t *rd, + const sqfs_inode_generic_t *inode, + sqfs_u32 flags) +{ + sqfs_u32 parent; + int ret; + + if (flags & (~SQFS_DIR_OPEN_ALL_FLAGS)) + return SQFS_ERROR_UNSUPPORTED; + + ret = sqfs_readdir_state_init(&rd->it, &rd->super, inode); + if (ret) + return ret; + + if ((rd->flags & SQFS_DIR_READER_DOT_ENTRIES) && + !(flags & SQFS_DIR_OPEN_NO_DOT_ENTRIES)) { + if (inode->base.type == SQFS_INODE_EXT_DIR) { + parent = inode->data.dir_ext.parent_inode; + } else { + parent = inode->data.dir.parent_inode; + } + + if (dcache_find(rd, inode->base.inode_number, &rd->cur_ref)) + return SQFS_ERROR_NO_ENTRY; + + if (rd->cur_ref == rd->super.root_inode_ref) { + rd->parent_ref = rd->cur_ref; + } else if (dcache_find(rd, parent, &rd->parent_ref)) { + return SQFS_ERROR_NO_ENTRY; + } + + rd->state = DIR_STATE_OPENED; + } else { + rd->state = DIR_STATE_ENTRIES; + } + + rd->start_state = rd->state; + return 0; +} + +static int mk_dummy_entry(const char *str, sqfs_dir_entry_t **out) +{ + size_t len = strlen(str); + sqfs_dir_entry_t *ent; + + ent = calloc(1, sizeof(sqfs_dir_entry_t) + len + 1); + if (ent == NULL) + return SQFS_ERROR_ALLOC; + + ent->type = SQFS_INODE_DIR; + ent->size = len - 1; + + strcpy((char *)ent->name, str); + + *out = ent; + return 0; +} + +int sqfs_dir_reader_read(sqfs_dir_reader_t *rd, sqfs_dir_entry_t **out) +{ + int err; + + switch (rd->state) { + case DIR_STATE_OPENED: + err = mk_dummy_entry(".", out); + if (err == 0) { + rd->state = DIR_STATE_DOT; + rd->ent_ref = rd->cur_ref; + } + return err; + case DIR_STATE_DOT: + err = mk_dummy_entry("..", out); + if (err == 0) { + rd->state = DIR_STATE_ENTRIES; + rd->ent_ref = rd->parent_ref; + } + return err; + case DIR_STATE_ENTRIES: + break; + default: + return SQFS_ERROR_SEQUENCE; + } + + return sqfs_meta_reader_readdir(rd->meta_dir, &rd->it, + out, NULL, &rd->ent_ref); +} + +int sqfs_dir_reader_rewind(sqfs_dir_reader_t *rd) +{ + if (rd->state == DIR_STATE_NONE) + return SQFS_ERROR_SEQUENCE; + + sqfs_readdir_state_reset(&rd->it); + rd->state = rd->start_state; + return 0; +} + +int sqfs_dir_reader_find(sqfs_dir_reader_t *rd, const char *name) +{ + sqfs_dir_entry_t *ent; + int ret; + + ret = sqfs_dir_reader_rewind(rd); + if (ret != 0) + return ret; + + do { + ret = sqfs_dir_reader_read(rd, &ent); + if (ret < 0) + return ret; + if (ret > 0) + return SQFS_ERROR_NO_ENTRY; + + ret = strcmp((const char *)ent->name, name); + free(ent); + } while (ret < 0); + + return ret == 0 ? 0 : SQFS_ERROR_NO_ENTRY; +} + +int sqfs_dir_reader_get_inode(sqfs_dir_reader_t *rd, + sqfs_inode_generic_t **inode) +{ + int ret; + + ret = sqfs_meta_reader_read_inode(rd->meta_inode, &rd->super, + rd->ent_ref >> 16, + rd->ent_ref & 0x0FFFF, inode); + if (ret != 0) + return ret; + + return dcache_add(rd, *inode, rd->ent_ref); +} + +int sqfs_dir_reader_get_root_inode(sqfs_dir_reader_t *rd, + sqfs_inode_generic_t **inode) +{ + sqfs_u64 block_start = rd->super.root_inode_ref >> 16; + sqfs_u16 offset = rd->super.root_inode_ref & 0xFFFF; + int ret; + + ret = sqfs_meta_reader_read_inode(rd->meta_inode, &rd->super, + block_start, offset, inode); + if (ret != 0) + return ret; + + return dcache_add(rd, *inode, rd->super.root_inode_ref); +} + +int sqfs_dir_reader_find_by_path(sqfs_dir_reader_t *rd, + const sqfs_inode_generic_t *start, + const char *path, sqfs_inode_generic_t **out) +{ + sqfs_inode_generic_t *inode; + const char *ptr; + int ret = 0; + char *name; + + if (start == NULL) { + ret = sqfs_dir_reader_get_root_inode(rd, &inode); + } else { + ret = inode_copy(start, &inode); + } + + if (ret) + return ret; + + for (; *path != '\0'; path = ptr) { + if (*path == '/') { + for (ptr = path; *ptr == '/'; ++ptr) + ; + continue; + } + + ret = sqfs_dir_reader_open_dir(rd, inode, 0); + free(inode); + if (ret) + return ret; + + ptr = strchrnul(path, '/'); + + name = strndup(path, ptr - path); + if (name == NULL) + return SQFS_ERROR_ALLOC; + + ret = sqfs_dir_reader_find(rd, name); + free(name); + if (ret) + return ret; + + ret = sqfs_dir_reader_get_inode(rd, &inode); + if (ret) + return ret; + } + + *out = inode; + return 0; +} diff --git a/lib/sqfs/src/dir_reader/get_path.c b/lib/sqfs/src/dir_reader/get_path.c new file mode 100644 index 0000000..847bfd3 --- /dev/null +++ b/lib/sqfs/src/dir_reader/get_path.c @@ -0,0 +1,81 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * get_path.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#define SQFS_BUILDING_DLL +#include "internal.h" + +#include +#include + +int sqfs_tree_node_get_path(const sqfs_tree_node_t *node, char **out) +{ + const sqfs_tree_node_t *it; + size_t clen, len = 0; + char *str, *ptr; + + *out = NULL; + + if (node == NULL) + return SQFS_ERROR_ARG_INVALID; + + for (it = node; it->parent != NULL; it = it->parent) { + if (it->parent == node) + return SQFS_ERROR_LINK_LOOP; + + /* non-root nodes must have a valid name */ + clen = strlen((const char *)it->name); + + if (clen == 0) + return SQFS_ERROR_CORRUPTED; + + if (strchr((const char *)it->name, '/') != NULL) + return SQFS_ERROR_CORRUPTED; + + if (it->name[0] == '.') { + if (clen == 1 || (clen == 2 && it->name[1] == '.')) + return SQFS_ERROR_CORRUPTED; + } + + /* compute total path length */ + if (SZ_ADD_OV(clen, 1, &clen)) + return SQFS_ERROR_OVERFLOW; + + if (SZ_ADD_OV(len, clen, &len)) + return SQFS_ERROR_OVERFLOW; + } + + /* root node must not have a name */ + if (it->name[0] != '\0') + return SQFS_ERROR_ARG_INVALID; + + /* generate the path */ + if (node->parent == NULL) { + str = strdup("/"); + if (str == NULL) + return SQFS_ERROR_ALLOC; + } else { + if (SZ_ADD_OV(len, 1, &len)) + return SQFS_ERROR_OVERFLOW; + + str = malloc(len); + if (str == NULL) + return SQFS_ERROR_ALLOC; + + ptr = str + len - 1; + *ptr = '\0'; + + for (it = node; it->parent != NULL; it = it->parent) { + len = strlen((const char *)it->name); + ptr -= len; + + memcpy(ptr, (const char *)it->name, len); + *(--ptr) = '/'; + } + } + + *out = str; + return 0; +} diff --git a/lib/sqfs/src/dir_reader/internal.h b/lib/sqfs/src/dir_reader/internal.h new file mode 100644 index 0000000..471d197 --- /dev/null +++ b/lib/sqfs/src/dir_reader/internal.h @@ -0,0 +1,52 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * internal.h + * + * Copyright (C) 2019 David Oberhollenzer + */ +#ifndef DIR_READER_INTERNAL_H +#define DIR_READER_INTERNAL_H + +#include "config.h" + +#include "sqfs/meta_reader.h" +#include "sqfs/dir_reader.h" +#include "sqfs/compressor.h" +#include "sqfs/id_table.h" +#include "sqfs/super.h" +#include "sqfs/inode.h" +#include "sqfs/error.h" +#include "sqfs/dir.h" +#include "util/rbtree.h" +#include "util/util.h" + +#include +#include + +enum { + DIR_STATE_NONE = 0, + DIR_STATE_OPENED = 1, + DIR_STATE_DOT = 2, + DIR_STATE_ENTRIES = 3, +}; + +struct sqfs_dir_reader_t { + sqfs_object_t base; + + sqfs_meta_reader_t *meta_dir; + sqfs_meta_reader_t *meta_inode; + sqfs_super_t super; + + sqfs_readdir_state_t it; + + sqfs_u32 flags; + + int start_state; + int state; + sqfs_u64 parent_ref; + sqfs_u64 cur_ref; + sqfs_u64 ent_ref; + rbtree_t dcache; +}; + +#endif /* DIR_READER_INTERNAL_H */ diff --git a/lib/sqfs/src/dir_reader/read_tree.c b/lib/sqfs/src/dir_reader/read_tree.c new file mode 100644 index 0000000..91cc2c0 --- /dev/null +++ b/lib/sqfs/src/dir_reader/read_tree.c @@ -0,0 +1,288 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * read_tree.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#define SQFS_BUILDING_DLL +#include "internal.h" + +static int should_skip(int type, unsigned int flags) +{ + switch (type) { + case SQFS_INODE_BDEV: + case SQFS_INODE_CDEV: + case SQFS_INODE_EXT_CDEV: + case SQFS_INODE_EXT_BDEV: + return (flags & SQFS_TREE_NO_DEVICES); + case SQFS_INODE_SLINK: + case SQFS_INODE_EXT_SLINK: + return (flags & SQFS_TREE_NO_SLINKS); + case SQFS_INODE_SOCKET: + case SQFS_INODE_EXT_SOCKET: + return(flags & SQFS_TREE_NO_SOCKETS); + case SQFS_INODE_FIFO: + case SQFS_INODE_EXT_FIFO: + return (flags & SQFS_TREE_NO_FIFO); + default: + break; + } + + return 0; +} + +static bool would_be_own_parent(sqfs_tree_node_t *parent, sqfs_tree_node_t *n) +{ + sqfs_u32 inum = n->inode->base.inode_number; + + while (parent != NULL) { + if (parent->inode->base.inode_number == inum) + return true; + + parent = parent->parent; + } + + return false; +} + +static sqfs_tree_node_t *create_node(sqfs_inode_generic_t *inode, + const char *name) +{ + sqfs_tree_node_t *n; + + n = alloc_flex(sizeof(*n), 1, strlen(name) + 1); + if (n == NULL) + return NULL; + + n->inode = inode; + strcpy((char *)n->name, name); + return n; +} + +static int fill_dir(sqfs_dir_reader_t *dr, sqfs_tree_node_t *root, + unsigned int flags) +{ + sqfs_tree_node_t *n, *prev, **tail; + sqfs_inode_generic_t *inode; + sqfs_dir_entry_t *ent; + int err; + + tail = &root->children; + + for (;;) { + err = sqfs_dir_reader_read(dr, &ent); + if (err > 0) + break; + if (err < 0) + return err; + + if (should_skip(ent->type, flags)) { + free(ent); + continue; + } + + err = sqfs_dir_reader_get_inode(dr, &inode); + if (err) { + free(ent); + return err; + } + + n = create_node(inode, (const char *)ent->name); + free(ent); + + if (n == NULL) { + free(inode); + return SQFS_ERROR_ALLOC; + } + + if (would_be_own_parent(root, n)) { + free(n); + free(inode); + return SQFS_ERROR_LINK_LOOP; + } + + *tail = n; + tail = &n->next; + n->parent = root; + } + + n = root->children; + prev = NULL; + + while (n != NULL) { + if (n->inode->base.type == SQFS_INODE_DIR || + n->inode->base.type == SQFS_INODE_EXT_DIR) { + if (!(flags & SQFS_TREE_NO_RECURSE)) { + err = sqfs_dir_reader_open_dir(dr, n->inode, + SQFS_DIR_OPEN_NO_DOT_ENTRIES); + if (err) + return err; + + err = fill_dir(dr, n, flags); + if (err) + return err; + } + + if (n->children == NULL && + (flags & SQFS_TREE_NO_EMPTY)) { + free(n->inode); + if (prev == NULL) { + root->children = root->children->next; + free(n); + n = root->children; + } else { + prev->next = n->next; + free(n); + n = prev->next; + } + continue; + } + } + + prev = n; + n = n->next; + } + + return 0; +} + +static int resolve_ids(sqfs_tree_node_t *root, const sqfs_id_table_t *idtbl) +{ + sqfs_tree_node_t *it; + int err; + + for (it = root->children; it != NULL; it = it->next) + resolve_ids(it, idtbl); + + err = sqfs_id_table_index_to_id(idtbl, root->inode->base.uid_idx, + &root->uid); + if (err) + return err; + + return sqfs_id_table_index_to_id(idtbl, root->inode->base.gid_idx, + &root->gid); +} + +void sqfs_dir_tree_destroy(sqfs_tree_node_t *root) +{ + sqfs_tree_node_t *it; + + if (!root) + return; + + while (root->children != NULL) { + it = root->children; + root->children = it->next; + + sqfs_dir_tree_destroy(it); + } + + free(root->inode); + free(root); +} + +int sqfs_dir_reader_get_full_hierarchy(sqfs_dir_reader_t *rd, + const sqfs_id_table_t *idtbl, + const char *path, unsigned int flags, + sqfs_tree_node_t **out) +{ + sqfs_tree_node_t *root, *tail, *new; + sqfs_inode_generic_t *inode; + sqfs_dir_entry_t *ent; + const char *ptr; + int ret; + + if (flags & ~SQFS_TREE_ALL_FLAGS) + return SQFS_ERROR_UNSUPPORTED; + + ret = sqfs_dir_reader_get_root_inode(rd, &inode); + if (ret) + return ret; + + root = tail = create_node(inode, ""); + if (root == NULL) { + free(inode); + return SQFS_ERROR_ALLOC; + } + inode = NULL; + + while (path != NULL && *path != '\0') { + if (*path == '/') { + while (*path == '/') + ++path; + continue; + } + + ret = sqfs_dir_reader_open_dir(rd, tail->inode, + SQFS_DIR_OPEN_NO_DOT_ENTRIES); + if (ret) + goto fail; + + ptr = strchrnul(path, '/'); + + for (;;) { + ret = sqfs_dir_reader_read(rd, &ent); + if (ret < 0) + goto fail; + if (ret > 0) { + ret = SQFS_ERROR_NO_ENTRY; + goto fail; + } + + ret = strncmp((const char *)ent->name, + path, ptr - path); + if (ret == 0 && ent->name[ptr - path] == '\0') + break; + free(ent); + } + + ret = sqfs_dir_reader_get_inode(rd, &inode); + if (ret) { + free(ent); + goto fail; + } + + new = create_node(inode, (const char *)ent->name); + free(ent); + + if (new == NULL) { + free(inode); + ret = SQFS_ERROR_ALLOC; + goto fail; + } + + inode = NULL; + path = ptr; + + if (flags & SQFS_TREE_STORE_PARENTS) { + tail->children = new; + new->parent = tail; + tail = new; + } else { + sqfs_dir_tree_destroy(root); + root = tail = new; + } + } + + if (tail->inode->base.type == SQFS_INODE_DIR || + tail->inode->base.type == SQFS_INODE_EXT_DIR) { + ret = sqfs_dir_reader_open_dir(rd, tail->inode, + SQFS_DIR_OPEN_NO_DOT_ENTRIES); + if (ret) + goto fail; + + ret = fill_dir(rd, tail, flags); + if (ret) + goto fail; + } + + ret = resolve_ids(root, idtbl); + if (ret) + goto fail; + + *out = root; + return 0; +fail: + sqfs_dir_tree_destroy(root); + return ret; +} diff --git a/lib/sqfs/src/dir_writer.c b/lib/sqfs/src/dir_writer.c new file mode 100644 index 0000000..d2b72df --- /dev/null +++ b/lib/sqfs/src/dir_writer.c @@ -0,0 +1,460 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * dir_writer.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#define SQFS_BUILDING_DLL +#include "config.h" + +#include "sqfs/meta_writer.h" +#include "sqfs/dir_writer.h" +#include "sqfs/super.h" +#include "sqfs/table.h" +#include "sqfs/inode.h" +#include "sqfs/error.h" +#include "sqfs/block.h" +#include "sqfs/dir.h" +#include "util/array.h" +#include "util/util.h" + +#include +#include + +#define DIR_INDEX_THRESHOLD (256) + +typedef struct dir_entry_t { + struct dir_entry_t *next; + sqfs_u64 inode_ref; + sqfs_u32 inode_num; + sqfs_u16 type; + size_t name_len; + char name[]; +} dir_entry_t; + +typedef struct index_ent_t { + struct index_ent_t *next; + dir_entry_t *ent; + sqfs_u64 block; + sqfs_u32 index; +} index_ent_t; + +struct sqfs_dir_writer_t { + sqfs_object_t base; + + dir_entry_t *list; + dir_entry_t *list_end; + + index_ent_t *idx; + index_ent_t *idx_end; + + sqfs_u64 dir_ref; + size_t dir_size; + size_t ent_count; + sqfs_meta_writer_t *dm; + + array_t export_tbl; +}; + +static int get_type(sqfs_u16 mode) +{ + switch (mode & S_IFMT) { + case S_IFSOCK: return SQFS_INODE_SOCKET; + case S_IFIFO: return SQFS_INODE_FIFO; + case S_IFLNK: return SQFS_INODE_SLINK; + case S_IFBLK: return SQFS_INODE_BDEV; + case S_IFCHR: return SQFS_INODE_CDEV; + case S_IFDIR: return SQFS_INODE_DIR; + case S_IFREG: return SQFS_INODE_FILE; + default: + break; + } + + return SQFS_ERROR_UNSUPPORTED; +} + +static void writer_reset(sqfs_dir_writer_t *writer) +{ + dir_entry_t *ent; + index_ent_t *idx; + + while (writer->idx != NULL) { + idx = writer->idx; + writer->idx = idx->next; + free(idx); + } + + while (writer->list != NULL) { + ent = writer->list; + writer->list = ent->next; + free(ent); + } + + writer->list_end = NULL; + writer->idx_end = NULL; + writer->dir_ref = 0; + writer->dir_size = 0; + writer->ent_count = 0; +} + +static int add_export_table_entry(sqfs_dir_writer_t *writer, + sqfs_u32 inum, sqfs_u64 iref) +{ + sqfs_u64 *ptr; + int ret; + + if (writer->export_tbl.data == NULL) + return 0; + + if (inum < 1) + return SQFS_ERROR_ARG_INVALID; + + ret = array_set_capacity(&writer->export_tbl, inum); + if (ret != 0) + return ret; + + ptr = (sqfs_u64 *)writer->export_tbl.data; + + if ((inum - 1) >= writer->export_tbl.used) { + memset(ptr + writer->export_tbl.used, 0xFF, + (inum - writer->export_tbl.used) * sizeof(*ptr)); + + writer->export_tbl.used = inum; + } + + ptr[inum - 1] = iref; + return 0; +} + +static void dir_writer_destroy(sqfs_object_t *obj) +{ + sqfs_dir_writer_t *writer = (sqfs_dir_writer_t *)obj; + + sqfs_drop(writer->dm); + writer_reset(writer); + array_cleanup(&writer->export_tbl); + free(writer); +} + +sqfs_dir_writer_t *sqfs_dir_writer_create(sqfs_meta_writer_t *dm, + sqfs_u32 flags) +{ + sqfs_dir_writer_t *writer; + + if (flags & ~SQFS_DIR_WRITER_CREATE_ALL_FLAGS) + return NULL; + + writer = calloc(1, sizeof(*writer)); + if (writer == NULL) + return NULL; + + sqfs_object_init(writer, dir_writer_destroy, NULL); + + if (flags & SQFS_DIR_WRITER_CREATE_EXPORT_TABLE) { + if (array_init(&writer->export_tbl, sizeof(sqfs_u64), 512)) { + free(writer); + return NULL; + } + + memset(writer->export_tbl.data, 0xFF, + writer->export_tbl.size * writer->export_tbl.count); + } + + writer->dm = sqfs_grab(dm); + return writer; +} + +int sqfs_dir_writer_begin(sqfs_dir_writer_t *writer, sqfs_u32 flags) +{ + sqfs_u32 offset; + sqfs_u64 block; + + if (flags != 0) + return SQFS_ERROR_UNSUPPORTED; + + writer_reset(writer); + + sqfs_meta_writer_get_position(writer->dm, &block, &offset); + writer->dir_ref = (block << 16) | offset; + return 0; +} + +int sqfs_dir_writer_add_entry(sqfs_dir_writer_t *writer, const char *name, + sqfs_u32 inode_num, sqfs_u64 inode_ref, + sqfs_u16 mode) +{ + dir_entry_t *ent; + int type, err; + + type = get_type(mode); + if (type < 0) + return type; + + if (name[0] == '\0' || inode_num < 1) + return SQFS_ERROR_ARG_INVALID; + + err = add_export_table_entry(writer, inode_num, inode_ref); + if (err) + return err; + + ent = alloc_flex(sizeof(*ent), 1, strlen(name)); + if (ent == NULL) + return SQFS_ERROR_ALLOC; + + ent->inode_ref = inode_ref; + ent->inode_num = inode_num; + ent->type = type; + ent->name_len = strlen(name); + memcpy(ent->name, name, ent->name_len); + + if (writer->list_end == NULL) { + writer->list = writer->list_end = ent; + } else { + writer->list_end->next = ent; + writer->list_end = ent; + } + + writer->ent_count += 1; + return 0; +} + +static size_t get_conseq_entry_count(sqfs_u32 offset, dir_entry_t *head) +{ + size_t size, count = 0; + dir_entry_t *it; + sqfs_s32 diff; + + size = (offset + sizeof(sqfs_dir_header_t)) % SQFS_META_BLOCK_SIZE; + + for (it = head; it != NULL; it = it->next) { + if ((it->inode_ref >> 16) != (head->inode_ref >> 16)) + break; + + diff = it->inode_num - head->inode_num; + + if (diff > 32767 || diff < -32767) + break; + + size += sizeof(sqfs_dir_entry_t) + it->name_len; + + if (count > 0 && size > SQFS_META_BLOCK_SIZE) + break; + + count += 1; + + if (count == SQFS_MAX_DIR_ENT) + break; + } + + return count; +} + +static int add_header(sqfs_dir_writer_t *writer, size_t count, + dir_entry_t *ref, sqfs_u64 block) +{ + sqfs_dir_header_t hdr; + index_ent_t *idx; + int err; + + hdr.count = htole32(count - 1); + hdr.start_block = htole32(ref->inode_ref >> 16); + hdr.inode_number = htole32(ref->inode_num); + + err = sqfs_meta_writer_append(writer->dm, &hdr, sizeof(hdr)); + if (err) + return err; + + idx = calloc(1, sizeof(*idx)); + if (idx == NULL) + return SQFS_ERROR_ALLOC; + + idx->ent = ref; + idx->block = block; + idx->index = writer->dir_size; + + if (writer->idx_end == NULL) { + writer->idx = writer->idx_end = idx; + } else { + writer->idx_end->next = idx; + writer->idx_end = idx; + } + + writer->dir_size += sizeof(hdr); + return 0; +} + +int sqfs_dir_writer_end(sqfs_dir_writer_t *writer) +{ + dir_entry_t *it, *first; + sqfs_dir_entry_t ent; + sqfs_u16 *diff_u16; + size_t i, count; + sqfs_u32 offset; + sqfs_u64 block; + int err; + + for (it = writer->list; it != NULL; ) { + sqfs_meta_writer_get_position(writer->dm, &block, &offset); + count = get_conseq_entry_count(offset, it); + + err = add_header(writer, count, it, block); + if (err) + return err; + + first = it; + + for (i = 0; i < count; ++i) { + ent.offset = htole16(it->inode_ref & 0x0000FFFF); + ent.inode_diff = it->inode_num - first->inode_num; + ent.type = htole16(it->type); + ent.size = htole16(it->name_len - 1); + + diff_u16 = (sqfs_u16 *)&ent.inode_diff; + *diff_u16 = htole16(*diff_u16); + + err = sqfs_meta_writer_append(writer->dm, &ent, + sizeof(ent)); + if (err) + return err; + + err = sqfs_meta_writer_append(writer->dm, it->name, + it->name_len); + if (err) + return err; + + writer->dir_size += sizeof(ent) + it->name_len; + it = it->next; + } + } + + return 0; +} + +size_t sqfs_dir_writer_get_size(const sqfs_dir_writer_t *writer) +{ + return writer->dir_size; +} + +sqfs_u64 sqfs_dir_writer_get_dir_reference(const sqfs_dir_writer_t *writer) +{ + return writer->dir_ref; +} + +size_t sqfs_dir_writer_get_index_size(const sqfs_dir_writer_t *writer) +{ + size_t index_size = 0; + index_ent_t *idx; + + for (idx = writer->idx; idx != NULL; idx = idx->next) + index_size += sizeof(sqfs_dir_index_t) + idx->ent->name_len; + + return index_size; +} + +size_t sqfs_dir_writer_get_entry_count(const sqfs_dir_writer_t *writer) +{ + return writer->ent_count; +} + +sqfs_inode_generic_t +*sqfs_dir_writer_create_inode(const sqfs_dir_writer_t *writer, + size_t hlinks, sqfs_u32 xattr, + sqfs_u32 parent_ino) +{ + sqfs_inode_generic_t *inode; + sqfs_dir_index_t ent; + sqfs_u64 start_block; + sqfs_u16 block_offset; + size_t index_size; + index_ent_t *idx; + sqfs_u8 *ptr; + + index_size = 0; + + for (idx = writer->idx; idx != NULL; idx = idx->next) + index_size += sizeof(ent) + idx->ent->name_len; + + inode = alloc_flex(sizeof(*inode), 1, index_size); + if (inode == NULL) + return NULL; + + inode->payload_bytes_available = index_size; + start_block = writer->dir_ref >> 16; + block_offset = writer->dir_ref & 0xFFFF; + + if (xattr != 0xFFFFFFFF || start_block > 0xFFFFFFFFUL || + writer->dir_size > (0xFFFF - 3)) { + inode->base.type = SQFS_INODE_EXT_DIR; + } else { + inode->base.type = SQFS_INODE_DIR; + } + + if (writer->ent_count >= DIR_INDEX_THRESHOLD) + inode->base.type = SQFS_INODE_EXT_DIR; + + if (inode->base.type == SQFS_INODE_DIR) { + inode->data.dir.start_block = start_block; + inode->data.dir.nlink = writer->ent_count + hlinks + 2; + inode->data.dir.size = writer->dir_size + 3; + inode->data.dir.offset = block_offset; + inode->data.dir.parent_inode = parent_ino; + } else { + inode->data.dir_ext.nlink = writer->ent_count + hlinks + 2; + inode->data.dir_ext.size = writer->dir_size + 3; + inode->data.dir_ext.start_block = start_block; + inode->data.dir_ext.parent_inode = parent_ino; + inode->data.dir_ext.offset = block_offset; + inode->data.dir_ext.xattr_idx = xattr; + inode->data.dir_ext.inodex_count = 0; + inode->payload_bytes_used = 0; + + for (idx = writer->idx; idx != NULL; idx = idx->next) { + memset(&ent, 0, sizeof(ent)); + ent.start_block = idx->block; + ent.index = idx->index; + ent.size = idx->ent->name_len - 1; + + ptr = (sqfs_u8 *)inode->extra + + inode->payload_bytes_used; + memcpy(ptr, &ent, sizeof(ent)); + memcpy(ptr + sizeof(ent), idx->ent->name, + idx->ent->name_len); + + inode->data.dir_ext.inodex_count += 1; + inode->payload_bytes_used += sizeof(ent); + inode->payload_bytes_used += idx->ent->name_len; + } + } + + return inode; +} + +int sqfs_dir_writer_write_export_table(sqfs_dir_writer_t *writer, + sqfs_file_t *file, + sqfs_compressor_t *cmp, + sqfs_u32 root_inode_num, + sqfs_u64 root_inode_ref, + sqfs_super_t *super) +{ + sqfs_u64 start; + size_t size; + int ret; + + ret = add_export_table_entry(writer, root_inode_num, root_inode_ref); + if (ret) + return 0; + + if (writer->export_tbl.data == NULL) + return 0; + + size = writer->export_tbl.size * writer->export_tbl.used; + + ret = sqfs_write_table(file, cmp, writer->export_tbl.data, + size, &start); + if (ret) + return ret; + + super->export_table_start = start; + super->flags |= SQFS_FLAG_EXPORTABLE; + return 0; +} diff --git a/lib/sqfs/src/frag_table.c b/lib/sqfs/src/frag_table.c new file mode 100644 index 0000000..151df28 --- /dev/null +++ b/lib/sqfs/src/frag_table.c @@ -0,0 +1,204 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * frag_table.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#define SQFS_BUILDING_DLL +#include "config.h" + +#include "sqfs/frag_table.h" +#include "sqfs/super.h" +#include "sqfs/table.h" +#include "sqfs/error.h" +#include "sqfs/block.h" +#include "compat.h" +#include "util/array.h" + +#include +#include + +struct sqfs_frag_table_t { + sqfs_object_t base; + + array_t table; +}; + +static void frag_table_destroy(sqfs_object_t *obj) +{ + sqfs_frag_table_t *tbl = (sqfs_frag_table_t *)obj; + + array_cleanup(&tbl->table); + free(tbl); +} + +static sqfs_object_t *frag_table_copy(const sqfs_object_t *obj) +{ + const sqfs_frag_table_t *tbl = (const sqfs_frag_table_t *)obj; + sqfs_frag_table_t *copy = calloc(1, sizeof(*copy)); + + if (copy == NULL) + return NULL; + + if (array_init_copy(©->table, &tbl->table)) { + free(copy); + return NULL; + } + + return (sqfs_object_t *)copy; +} + +sqfs_frag_table_t *sqfs_frag_table_create(sqfs_u32 flags) +{ + sqfs_frag_table_t *tbl; + + if (flags != 0) + return NULL; + + tbl = calloc(1, sizeof(*tbl)); + if (tbl == NULL) + return NULL; + + sqfs_object_init(tbl, frag_table_destroy, frag_table_copy); + + array_init(&tbl->table, sizeof(sqfs_fragment_t), 0); + return tbl; +} + +int sqfs_frag_table_read(sqfs_frag_table_t *tbl, sqfs_file_t *file, + const sqfs_super_t *super, sqfs_compressor_t *cmp) +{ + sqfs_u64 location, lower, upper; + void *raw = NULL; + size_t size; + int err; + + array_cleanup(&tbl->table); + tbl->table.size = sizeof(sqfs_fragment_t); + + if (super->flags & SQFS_FLAG_NO_FRAGMENTS) + return 0; + + if (super->fragment_table_start == 0xFFFFFFFFFFFFFFFFUL) + return 0; + + if (super->fragment_entry_count == 0) + return 0; + + if (super->fragment_table_start >= super->bytes_used) + return SQFS_ERROR_OUT_OF_BOUNDS; + + /* location must be after inode & directory table, + but before the ID table */ + if (super->fragment_table_start < super->directory_table_start) + return SQFS_ERROR_CORRUPTED; + + if (super->fragment_table_start >= super->id_table_start) + return SQFS_ERROR_CORRUPTED; + + location = super->fragment_table_start; + lower = super->directory_table_start; + upper = super->id_table_start; + + if (super->export_table_start < super->id_table_start) + upper = super->export_table_start; + + if (SZ_MUL_OV(super->fragment_entry_count, sizeof(sqfs_fragment_t), + &size)) { + return SQFS_ERROR_OVERFLOW; + } + + err = sqfs_read_table(file, cmp, size, location, lower, upper, &raw); + if (err) { + free(raw); + return err; + } + + tbl->table.data = raw; + tbl->table.count = super->fragment_entry_count; + tbl->table.used = super->fragment_entry_count; + return 0; +} + +int sqfs_frag_table_write(sqfs_frag_table_t *tbl, sqfs_file_t *file, + sqfs_super_t *super, sqfs_compressor_t *cmp) +{ + size_t i; + int err; + + if (tbl->table.used == 0) { + super->fragment_table_start = 0xFFFFFFFFFFFFFFFF; + super->flags |= SQFS_FLAG_NO_FRAGMENTS; + super->flags &= ~SQFS_FLAG_ALWAYS_FRAGMENTS; + super->flags &= ~SQFS_FLAG_UNCOMPRESSED_FRAGMENTS; + return 0; + } + + err = sqfs_write_table(file, cmp, tbl->table.data, + tbl->table.size * tbl->table.used, + &super->fragment_table_start); + if (err) + return err; + + super->fragment_entry_count = tbl->table.used; + super->flags &= ~SQFS_FLAG_NO_FRAGMENTS; + super->flags |= SQFS_FLAG_ALWAYS_FRAGMENTS; + super->flags |= SQFS_FLAG_UNCOMPRESSED_FRAGMENTS; + + for (i = 0; i < tbl->table.used; ++i) { + sqfs_u32 sz = ((sqfs_fragment_t *)tbl->table.data)[i].size; + + if (SQFS_IS_BLOCK_COMPRESSED(le32toh(sz))) { + super->flags &= ~SQFS_FLAG_UNCOMPRESSED_FRAGMENTS; + break; + } + } + + return 0; +} + +int sqfs_frag_table_lookup(sqfs_frag_table_t *tbl, sqfs_u32 index, + sqfs_fragment_t *out) +{ + sqfs_fragment_t *frag = array_get(&tbl->table, index); + + if (frag == NULL) + return SQFS_ERROR_OUT_OF_BOUNDS; + + out->start_offset = le64toh(frag->start_offset); + out->size = le32toh(frag->size); + out->pad0 = le32toh(frag->pad0); + return 0; +} + +int sqfs_frag_table_append(sqfs_frag_table_t *tbl, sqfs_u64 location, + sqfs_u32 size, sqfs_u32 *index) +{ + sqfs_fragment_t frag; + + if (index != NULL) + *index = tbl->table.used; + + memset(&frag, 0, sizeof(frag)); + frag.start_offset = htole64(location); + frag.size = htole32(size); + + return array_append(&tbl->table, &frag); +} + +int sqfs_frag_table_set(sqfs_frag_table_t *tbl, sqfs_u32 index, + sqfs_u64 location, sqfs_u32 size) +{ + sqfs_fragment_t frag; + + memset(&frag, 0, sizeof(frag)); + frag.start_offset = htole64(location); + frag.size = htole32(size); + + return array_set(&tbl->table, index, &frag); +} + +size_t sqfs_frag_table_get_size(sqfs_frag_table_t *tbl) +{ + return tbl->table.used; +} diff --git a/lib/sqfs/src/id_table.c b/lib/sqfs/src/id_table.c new file mode 100644 index 0000000..ec3fdfe --- /dev/null +++ b/lib/sqfs/src/id_table.c @@ -0,0 +1,162 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * id_table.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#define SQFS_BUILDING_DLL +#include "config.h" + +#include "sqfs/id_table.h" +#include "sqfs/super.h" +#include "sqfs/table.h" +#include "sqfs/error.h" +#include "compat.h" +#include "util/array.h" + +#include +#include + +struct sqfs_id_table_t { + sqfs_object_t base; + + array_t ids; +}; + +static void id_table_destroy(sqfs_object_t *obj) +{ + sqfs_id_table_t *tbl = (sqfs_id_table_t *)obj; + + array_cleanup(&tbl->ids); + free(tbl); +} + +static sqfs_object_t *id_table_copy(const sqfs_object_t *obj) +{ + const sqfs_id_table_t *tbl = (const sqfs_id_table_t *)obj; + sqfs_id_table_t *copy = calloc(1, sizeof(*copy)); + + if (copy == NULL) + return NULL; + + if (array_init_copy(©->ids, &tbl->ids) != 0) { + free(copy); + return NULL; + } + + return (sqfs_object_t *)copy; +} + +sqfs_id_table_t *sqfs_id_table_create(sqfs_u32 flags) +{ + sqfs_id_table_t *tbl; + + if (flags != 0) + return NULL; + + tbl = calloc(1, sizeof(sqfs_id_table_t)); + + if (tbl != NULL) { + array_init(&tbl->ids, sizeof(sqfs_u32), 0); + sqfs_object_init(tbl, id_table_destroy, id_table_copy); + } + + return tbl; +} + +int sqfs_id_table_id_to_index(sqfs_id_table_t *tbl, sqfs_u32 id, sqfs_u16 *out) +{ + size_t i; + + for (i = 0; i < tbl->ids.used; ++i) { + if (((sqfs_u32 *)tbl->ids.data)[i] == id) { + *out = i; + return 0; + } + } + + if (tbl->ids.used == 0x10000) + return SQFS_ERROR_OVERFLOW; + + *out = tbl->ids.used; + return array_append(&tbl->ids, &id); +} + +int sqfs_id_table_index_to_id(const sqfs_id_table_t *tbl, sqfs_u16 index, + sqfs_u32 *out) +{ + if (index >= tbl->ids.used) + return SQFS_ERROR_OUT_OF_BOUNDS; + + *out = ((sqfs_u32 *)tbl->ids.data)[index]; + return 0; +} + +int sqfs_id_table_read(sqfs_id_table_t *tbl, sqfs_file_t *file, + const sqfs_super_t *super, sqfs_compressor_t *cmp) +{ + sqfs_u64 upper_limit, lower_limit; + void *raw_ids; + size_t i; + int ret; + + if (!super->id_count || super->id_table_start >= super->bytes_used) + return SQFS_ERROR_CORRUPTED; + + upper_limit = super->id_table_start; + lower_limit = super->directory_table_start; + + if (super->fragment_table_start > lower_limit && + super->fragment_table_start < upper_limit) { + lower_limit = super->fragment_table_start; + } + + if (super->export_table_start > lower_limit && + super->export_table_start < upper_limit) { + lower_limit = super->export_table_start; + } + + array_cleanup(&tbl->ids); + tbl->ids.size = sizeof(sqfs_u32); + + ret = sqfs_read_table(file, cmp, super->id_count * sizeof(sqfs_u32), + super->id_table_start, lower_limit, + upper_limit, &raw_ids); + if (ret) + return ret; + + for (i = 0; i < super->id_count; ++i) + ((sqfs_u32 *)raw_ids)[i] = le32toh(((sqfs_u32 *)raw_ids)[i]); + + tbl->ids.data = raw_ids; + tbl->ids.used = super->id_count; + tbl->ids.count = super->id_count; + return 0; +} + +int sqfs_id_table_write(sqfs_id_table_t *tbl, sqfs_file_t *file, + sqfs_super_t *super, sqfs_compressor_t *cmp) +{ + sqfs_u64 start; + size_t i; + int ret; + + for (i = 0; i < tbl->ids.used; ++i) { + ((sqfs_u32 *)tbl->ids.data)[i] = + htole32(((sqfs_u32 *)tbl->ids.data)[i]); + } + + super->id_count = tbl->ids.used; + + ret = sqfs_write_table(file, cmp, tbl->ids.data, + sizeof(sqfs_u32) * tbl->ids.used, &start); + + super->id_table_start = start; + + for (i = 0; i < tbl->ids.used; ++i) { + ((sqfs_u32 *)tbl->ids.data)[i] = + le32toh(((sqfs_u32 *)tbl->ids.data)[i]); + } + + return ret; +} diff --git a/lib/sqfs/src/inode.c b/lib/sqfs/src/inode.c new file mode 100644 index 0000000..ce51cf5 --- /dev/null +++ b/lib/sqfs/src/inode.c @@ -0,0 +1,380 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * inode.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#define SQFS_BUILDING_DLL +#include "config.h" + +#include "sqfs/inode.h" +#include "sqfs/error.h" +#include "sqfs/dir.h" + +#include +#include + +#include "util/util.h" + +static int inverse_type[] = { + [SQFS_INODE_DIR] = SQFS_INODE_EXT_DIR, + [SQFS_INODE_FILE] = SQFS_INODE_EXT_FILE, + [SQFS_INODE_SLINK] = SQFS_INODE_EXT_SLINK, + [SQFS_INODE_BDEV] = SQFS_INODE_EXT_BDEV, + [SQFS_INODE_CDEV] = SQFS_INODE_EXT_CDEV, + [SQFS_INODE_FIFO] = SQFS_INODE_EXT_FIFO, + [SQFS_INODE_SOCKET] = SQFS_INODE_EXT_SOCKET, + [SQFS_INODE_EXT_DIR] = SQFS_INODE_DIR, + [SQFS_INODE_EXT_FILE] = SQFS_INODE_FILE, + [SQFS_INODE_EXT_SLINK] = SQFS_INODE_SLINK, + [SQFS_INODE_EXT_BDEV] = SQFS_INODE_BDEV, + [SQFS_INODE_EXT_CDEV] = SQFS_INODE_CDEV, + [SQFS_INODE_EXT_FIFO] = SQFS_INODE_FIFO, + [SQFS_INODE_EXT_SOCKET] = SQFS_INODE_SOCKET, +}; + +int sqfs_inode_get_xattr_index(const sqfs_inode_generic_t *inode, + sqfs_u32 *out) +{ + switch (inode->base.type) { + case SQFS_INODE_DIR: + case SQFS_INODE_FILE: + case SQFS_INODE_SLINK: + case SQFS_INODE_BDEV: + case SQFS_INODE_CDEV: + case SQFS_INODE_FIFO: + case SQFS_INODE_SOCKET: + *out = 0xFFFFFFFF; + break; + case SQFS_INODE_EXT_DIR: + *out = inode->data.dir_ext.xattr_idx; + break; + case SQFS_INODE_EXT_FILE: + *out = inode->data.file_ext.xattr_idx; + break; + case SQFS_INODE_EXT_SLINK: + *out = inode->data.slink_ext.xattr_idx; + break; + case SQFS_INODE_EXT_BDEV: + case SQFS_INODE_EXT_CDEV: + *out = inode->data.dev_ext.xattr_idx; + break; + case SQFS_INODE_EXT_FIFO: + case SQFS_INODE_EXT_SOCKET: + *out = inode->data.ipc_ext.xattr_idx; + break; + default: + return SQFS_ERROR_CORRUPTED; + } + + return 0; +} + +int sqfs_inode_set_xattr_index(sqfs_inode_generic_t *inode, sqfs_u32 index) +{ + int err; + + if (index != 0xFFFFFFFF) { + err = sqfs_inode_make_extended(inode); + if (err) + return err; + } + + switch (inode->base.type) { + case SQFS_INODE_DIR: + case SQFS_INODE_FILE: + case SQFS_INODE_SLINK: + case SQFS_INODE_BDEV: + case SQFS_INODE_CDEV: + case SQFS_INODE_FIFO: + case SQFS_INODE_SOCKET: + break; + case SQFS_INODE_EXT_DIR: + inode->data.dir_ext.xattr_idx = index; + break; + case SQFS_INODE_EXT_FILE: + inode->data.file_ext.xattr_idx = index; + break; + case SQFS_INODE_EXT_SLINK: + inode->data.slink_ext.xattr_idx = index; + break; + case SQFS_INODE_EXT_BDEV: + case SQFS_INODE_EXT_CDEV: + inode->data.dev_ext.xattr_idx = index; + break; + case SQFS_INODE_EXT_FIFO: + case SQFS_INODE_EXT_SOCKET: + inode->data.ipc_ext.xattr_idx = index; + break; + default: + return SQFS_ERROR_CORRUPTED; + } + + return 0; +} + +int sqfs_inode_make_extended(sqfs_inode_generic_t *inode) +{ + switch (inode->base.type) { + case SQFS_INODE_DIR: { + sqfs_inode_dir_ext_t temp = { + .nlink = inode->data.dir.nlink, + .size = inode->data.dir.size, + .start_block = inode->data.dir.start_block, + .parent_inode = inode->data.dir.parent_inode, + .inodex_count = 0, + .offset = inode->data.dir.offset, + .xattr_idx = 0xFFFFFFFF, + }; + inode->data.dir_ext = temp; + break; + } + case SQFS_INODE_FILE: { + sqfs_inode_file_ext_t temp = { + .blocks_start = inode->data.file.blocks_start, + .file_size = inode->data.file.file_size, + .sparse = 0, + .nlink = 1, + .fragment_idx = inode->data.file.fragment_index, + .fragment_offset = inode->data.file.fragment_offset, + .xattr_idx = 0xFFFFFFFF, + }; + inode->data.file_ext = temp; + break; + } + case SQFS_INODE_SLINK: + inode->data.slink_ext.xattr_idx = 0xFFFFFFFF; + break; + case SQFS_INODE_BDEV: + case SQFS_INODE_CDEV: + inode->data.dev_ext.xattr_idx = 0xFFFFFFFF; + break; + case SQFS_INODE_FIFO: + case SQFS_INODE_SOCKET: + inode->data.dev_ext.xattr_idx = 0xFFFFFFFF; + break; + case SQFS_INODE_EXT_DIR: + case SQFS_INODE_EXT_FILE: + case SQFS_INODE_EXT_SLINK: + case SQFS_INODE_EXT_BDEV: + case SQFS_INODE_EXT_CDEV: + case SQFS_INODE_EXT_FIFO: + case SQFS_INODE_EXT_SOCKET: + return 0; + default: + return SQFS_ERROR_CORRUPTED; + } + + inode->base.type = inverse_type[inode->base.type]; + return 0; +} + +int sqfs_inode_make_basic(sqfs_inode_generic_t *inode) +{ + sqfs_u32 xattr; + int err; + + err = sqfs_inode_get_xattr_index(inode, &xattr); + if (err != 0 || xattr != 0xFFFFFFFF) + return err; + + switch (inode->base.type) { + case SQFS_INODE_DIR: + case SQFS_INODE_FILE: + case SQFS_INODE_SLINK: + case SQFS_INODE_BDEV: + case SQFS_INODE_CDEV: + case SQFS_INODE_FIFO: + case SQFS_INODE_SOCKET: + return 0; + case SQFS_INODE_EXT_DIR: { + sqfs_inode_dir_t temp = { + .start_block = inode->data.dir_ext.start_block, + .nlink = inode->data.dir_ext.nlink, + .size = inode->data.dir_ext.size, + .offset = inode->data.dir_ext.offset, + .parent_inode = inode->data.dir_ext.parent_inode, + }; + + if (inode->data.dir_ext.size > 0x0FFFF) + return 0; + + inode->data.dir = temp; + break; + } + case SQFS_INODE_EXT_FILE: { + sqfs_inode_file_t temp = { + .blocks_start = inode->data.file_ext.blocks_start, + .fragment_index = inode->data.file_ext.fragment_idx, + .fragment_offset = inode->data.file_ext.fragment_offset, + .file_size = inode->data.file_ext.file_size, + }; + + if (inode->data.file_ext.blocks_start > 0x0FFFFFFFFUL) + return 0; + if (inode->data.file_ext.file_size > 0x0FFFFFFFFUL) + return 0; + if (inode->data.file_ext.sparse > 0) + return 0; + if (inode->data.file_ext.nlink > 1) + return 0; + + inode->data.file = temp; + break; + } + case SQFS_INODE_EXT_SLINK: + case SQFS_INODE_EXT_BDEV: + case SQFS_INODE_EXT_CDEV: + case SQFS_INODE_EXT_FIFO: + case SQFS_INODE_EXT_SOCKET: + break; + default: + return SQFS_ERROR_CORRUPTED; + } + + inode->base.type = inverse_type[inode->base.type]; + return 0; +} + +int sqfs_inode_set_file_size(sqfs_inode_generic_t *inode, sqfs_u64 size) +{ + if (inode->base.type == SQFS_INODE_EXT_FILE) { + inode->data.file_ext.file_size = size; + + if (size < 0x0FFFFFFFFUL) + sqfs_inode_make_basic(inode); + } else if (inode->base.type == SQFS_INODE_FILE) { + if (size > 0x0FFFFFFFFUL) { + sqfs_inode_make_extended(inode); + inode->data.file_ext.file_size = size; + } else { + inode->data.file.file_size = size; + } + } else { + return SQFS_ERROR_NOT_FILE; + } + + return 0; +} + +int sqfs_inode_set_frag_location(sqfs_inode_generic_t *inode, + sqfs_u32 index, sqfs_u32 offset) +{ + if (inode->base.type == SQFS_INODE_EXT_FILE) { + inode->data.file_ext.fragment_idx = index; + inode->data.file_ext.fragment_offset = offset; + } else if (inode->base.type == SQFS_INODE_FILE) { + inode->data.file.fragment_index = index; + inode->data.file.fragment_offset = offset; + } else { + return SQFS_ERROR_NOT_FILE; + } + + return 0; +} + +int sqfs_inode_set_file_block_start(sqfs_inode_generic_t *inode, + sqfs_u64 location) +{ + if (inode->base.type == SQFS_INODE_EXT_FILE) { + inode->data.file_ext.blocks_start = location; + + if (location < 0x0FFFFFFFFUL) + sqfs_inode_make_basic(inode); + } else if (inode->base.type == SQFS_INODE_FILE) { + if (location > 0x0FFFFFFFFUL) { + sqfs_inode_make_extended(inode); + inode->data.file_ext.blocks_start = location; + } else { + inode->data.file.blocks_start = location; + } + } else { + return SQFS_ERROR_NOT_FILE; + } + + return 0; +} + +int sqfs_inode_get_file_size(const sqfs_inode_generic_t *inode, sqfs_u64 *size) +{ + if (inode->base.type == SQFS_INODE_EXT_FILE) { + *size = inode->data.file_ext.file_size; + } else if (inode->base.type == SQFS_INODE_FILE) { + *size = inode->data.file.file_size; + } else { + return SQFS_ERROR_NOT_FILE; + } + + return 0; +} + +int sqfs_inode_get_frag_location(const sqfs_inode_generic_t *inode, + sqfs_u32 *index, sqfs_u32 *offset) +{ + if (inode->base.type == SQFS_INODE_EXT_FILE) { + *index = inode->data.file_ext.fragment_idx; + *offset = inode->data.file_ext.fragment_offset; + } else if (inode->base.type == SQFS_INODE_FILE) { + *index = inode->data.file.fragment_index; + *offset = inode->data.file.fragment_offset; + } else { + return SQFS_ERROR_NOT_FILE; + } + + return 0; +} + +int sqfs_inode_get_file_block_start(const sqfs_inode_generic_t *inode, + sqfs_u64 *location) +{ + if (inode->base.type == SQFS_INODE_EXT_FILE) { + *location = inode->data.file_ext.blocks_start; + } else if (inode->base.type == SQFS_INODE_FILE) { + *location = inode->data.file.blocks_start; + } else { + return SQFS_ERROR_NOT_FILE; + } + + return 0; +} + +int sqfs_inode_unpack_dir_index_entry(const sqfs_inode_generic_t *inode, + sqfs_dir_index_t **out, + size_t index) +{ + sqfs_dir_index_t ent; + const char *ptr; + size_t offset; + + if (inode->base.type != SQFS_INODE_EXT_DIR) { + if (inode->base.type == SQFS_INODE_DIR) + return SQFS_ERROR_OUT_OF_BOUNDS; + + return SQFS_ERROR_NOT_DIR; + } + + offset = 0; + ptr = (const char *)inode->extra; + + for (;;) { + if (offset >= inode->payload_bytes_used) + return SQFS_ERROR_OUT_OF_BOUNDS; + + if (index == 0) + break; + + memcpy(&ent, ptr + offset, sizeof(ent)); + offset += sizeof(ent) + ent.size + 1; + index -= 1; + } + + memcpy(&ent, ptr + offset, sizeof(ent)); + + *out = alloc_flex(sizeof(ent), 1, ent.size + 2); + if (*out == NULL) + return SQFS_ERROR_ALLOC; + + memcpy(*out, &ent, sizeof(ent)); + memcpy((*out)->name, ptr + offset + sizeof(ent), ent.size + 1); + return 0; +} diff --git a/lib/sqfs/src/meta_reader.c b/lib/sqfs/src/meta_reader.c new file mode 100644 index 0000000..e431d40 --- /dev/null +++ b/lib/sqfs/src/meta_reader.c @@ -0,0 +1,191 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * meta_reader.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#define SQFS_BUILDING_DLL +#include "config.h" + +#include "sqfs/meta_reader.h" +#include "sqfs/compressor.h" +#include "sqfs/error.h" +#include "sqfs/block.h" +#include "sqfs/io.h" +#include "util/util.h" + +#include +#include + +struct sqfs_meta_reader_t { + sqfs_object_t base; + + sqfs_u64 start; + sqfs_u64 limit; + size_t data_used; + + /* The location of the current block in the image */ + sqfs_u64 block_offset; + + /* The location of the next block after the current one */ + sqfs_u64 next_block; + + /* A byte offset into the uncompressed data of the current block */ + size_t offset; + + /* The underlying file descriptor to read from */ + sqfs_file_t *file; + + /* A pointer to the compressor to use for extracting data */ + sqfs_compressor_t *cmp; + + /* The raw data read from the input file */ + sqfs_u8 data[SQFS_META_BLOCK_SIZE]; + + /* The uncompressed data read from the input file */ + sqfs_u8 scratch[SQFS_META_BLOCK_SIZE]; +}; + +static void meta_reader_destroy(sqfs_object_t *m) +{ + sqfs_meta_reader_t *mr = (sqfs_meta_reader_t *)m; + + sqfs_drop(mr->file); + sqfs_drop(mr->cmp); + free(m); +} + +static sqfs_object_t *meta_reader_copy(const sqfs_object_t *obj) +{ + const sqfs_meta_reader_t *m = (const sqfs_meta_reader_t *)obj; + sqfs_meta_reader_t *copy = malloc(sizeof(*copy)); + + if (copy != NULL) { + memcpy(copy, m, sizeof(*m)); + + /* duplicate references */ + copy->cmp = sqfs_grab(copy->cmp); + copy->file = sqfs_grab(copy->file); + } + + return (sqfs_object_t *)copy; +} + +sqfs_meta_reader_t *sqfs_meta_reader_create(sqfs_file_t *file, + sqfs_compressor_t *cmp, + sqfs_u64 start, sqfs_u64 limit) +{ + sqfs_meta_reader_t *m = calloc(1, sizeof(*m)); + + if (m == NULL) + return NULL; + + sqfs_object_init(m, meta_reader_destroy, meta_reader_copy); + + m->block_offset = 0xFFFFFFFFFFFFFFFFUL; + m->start = start; + m->limit = limit; + m->file = sqfs_grab(file); + m->cmp = sqfs_grab(cmp); + return m; +} + +int sqfs_meta_reader_seek(sqfs_meta_reader_t *m, sqfs_u64 block_start, + size_t offset) +{ + bool compressed; + sqfs_u16 header; + sqfs_u32 size; + sqfs_s32 ret; + int err; + + if (block_start < m->start || block_start >= m->limit) + return SQFS_ERROR_OUT_OF_BOUNDS; + + if (block_start == m->block_offset) { + if (offset >= m->data_used) + return SQFS_ERROR_OUT_OF_BOUNDS; + + m->offset = offset; + return 0; + } + + err = m->file->read_at(m->file, block_start, &header, 2); + if (err) + return err; + + header = le16toh(header); + compressed = (header & 0x8000) == 0; + size = header & 0x7FFF; + + if (size > sizeof(m->data)) + return SQFS_ERROR_CORRUPTED; + + if ((block_start + 2 + size) > m->limit) + return SQFS_ERROR_OUT_OF_BOUNDS; + + err = m->file->read_at(m->file, block_start + 2, m->data, size); + if (err) + return err; + + if (compressed) { + ret = m->cmp->do_block(m->cmp, m->data, size, + m->scratch, sizeof(m->scratch)); + + if (ret < 0) + return ret; + + memcpy(m->data, m->scratch, ret); + m->data_used = ret; + } else { + m->data_used = size; + } + + if (offset >= m->data_used) + return SQFS_ERROR_OUT_OF_BOUNDS; + + m->block_offset = block_start; + m->next_block = block_start + size + 2; + m->offset = offset; + return 0; +} + +void sqfs_meta_reader_get_position(const sqfs_meta_reader_t *m, + sqfs_u64 *block_start, size_t *offset) +{ + if (m->offset == m->data_used) { + *block_start = m->next_block; + *offset = 0; + } else { + *block_start = m->block_offset; + *offset = m->offset; + } +} + +int sqfs_meta_reader_read(sqfs_meta_reader_t *m, void *data, size_t size) +{ + size_t diff; + int ret; + + while (size != 0) { + diff = m->data_used - m->offset; + + if (diff == 0) { + ret = sqfs_meta_reader_seek(m, m->next_block, 0); + if (ret) + return ret; + diff = m->data_used; + } + + if (diff > size) + diff = size; + + memcpy(data, m->data + m->offset, diff); + + m->offset += diff; + data = (char *)data + diff; + size -= diff; + } + + return 0; +} diff --git a/lib/sqfs/src/meta_writer.c b/lib/sqfs/src/meta_writer.c new file mode 100644 index 0000000..bf3f426 --- /dev/null +++ b/lib/sqfs/src/meta_writer.c @@ -0,0 +1,215 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * meta_writer.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#define SQFS_BUILDING_DLL +#include "config.h" + +#include "sqfs/meta_writer.h" +#include "sqfs/compressor.h" +#include "sqfs/error.h" +#include "sqfs/block.h" +#include "sqfs/io.h" +#include "util/util.h" + +#include +#include + +typedef struct meta_block_t { + struct meta_block_t *next; + + /* possibly compressed data with 2 byte header */ + sqfs_u8 data[SQFS_META_BLOCK_SIZE + 2]; +} meta_block_t; + +struct sqfs_meta_writer_t { + sqfs_object_t base; + + /* A byte offset into the uncompressed data of the current block */ + size_t offset; + + /* The location of the current block in the file */ + size_t block_offset; + + /* The underlying file descriptor to write to */ + sqfs_file_t *file; + + /* A pointer to the compressor to use for compressing the data */ + sqfs_compressor_t *cmp; + + /* The raw data chunk that data is appended to */ + sqfs_u8 data[SQFS_META_BLOCK_SIZE]; + + sqfs_u32 flags; + meta_block_t *list; + meta_block_t *list_end; +}; + +static int write_block(sqfs_file_t *file, meta_block_t *outblk) +{ + sqfs_u16 header; + size_t count; + sqfs_u64 off; + + memcpy(&header, outblk->data, sizeof(header)); + count = le16toh(header) & 0x7FFF; + off = file->get_size(file); + + return file->write_at(file, off, outblk->data, count + 2); +} + +static void meta_writer_destroy(sqfs_object_t *obj) +{ + sqfs_meta_writer_t *m = (sqfs_meta_writer_t *)obj; + meta_block_t *blk; + + while (m->list != NULL) { + blk = m->list; + m->list = blk->next; + free(blk); + } + + sqfs_drop(m->file); + sqfs_drop(m->cmp); + free(m); +} + +sqfs_meta_writer_t *sqfs_meta_writer_create(sqfs_file_t *file, + sqfs_compressor_t *cmp, + sqfs_u32 flags) +{ + sqfs_meta_writer_t *m; + + if (flags & ~SQFS_META_WRITER_ALL_FLAGS) + return NULL; + + m = calloc(1, sizeof(*m)); + if (m == NULL) + return NULL; + + sqfs_object_init(m, meta_writer_destroy, NULL); + + m->cmp = sqfs_grab(cmp); + m->file = sqfs_grab(file); + m->flags = flags; + return m; +} + +int sqfs_meta_writer_flush(sqfs_meta_writer_t *m) +{ + meta_block_t *outblk; + sqfs_u16 header; + sqfs_u32 count; + sqfs_s32 ret; + + if (m->offset == 0) + return 0; + + outblk = calloc(1, sizeof(*outblk)); + if (outblk == NULL) + return SQFS_ERROR_ALLOC; + + ret = m->cmp->do_block(m->cmp, m->data, m->offset, + outblk->data + 2, sizeof(outblk->data) - 2); + if (ret < 0) { + free(outblk); + return ret; + } + + if (ret > 0) { + header = htole16(ret); + count = ret + 2; + } else { + header = htole16(m->offset | 0x8000); + memcpy(outblk->data + 2, m->data, m->offset); + count = m->offset + 2; + } + + memcpy(outblk->data, &header, sizeof(header)); + + ret = 0; + + if (m->flags & SQFS_META_WRITER_KEEP_IN_MEMORY) { + if (m->list == NULL) { + m->list = outblk; + } else { + m->list_end->next = outblk; + } + m->list_end = outblk; + } else { + ret = write_block(m->file, outblk); + free(outblk); + } + + memset(m->data, 0, sizeof(m->data)); + m->offset = 0; + m->block_offset += count; + return ret; +} + +int sqfs_meta_writer_append(sqfs_meta_writer_t *m, const void *data, + size_t size) +{ + size_t diff; + int ret; + + while (size != 0) { + diff = sizeof(m->data) - m->offset; + + if (diff == 0) { + ret = sqfs_meta_writer_flush(m); + if (ret) + return ret; + diff = sizeof(m->data); + } + + if (diff > size) + diff = size; + + memcpy(m->data + m->offset, data, diff); + m->offset += diff; + size -= diff; + data = (const char *)data + diff; + } + + if (m->offset == sizeof(m->data)) + return sqfs_meta_writer_flush(m); + + return 0; +} + +void sqfs_meta_writer_get_position(const sqfs_meta_writer_t *m, + sqfs_u64 *block_start, + sqfs_u32 *offset) +{ + *block_start = m->block_offset; + *offset = m->offset; +} + +void sqfs_meta_writer_reset(sqfs_meta_writer_t *m) +{ + m->block_offset = 0; + m->offset = 0; +} + +int sqfs_meta_write_write_to_file(sqfs_meta_writer_t *m) +{ + meta_block_t *blk; + int ret; + + while (m->list != NULL) { + blk = m->list; + + ret = write_block(m->file, blk); + if (ret) + return ret; + + m->list = blk->next; + free(blk); + } + + m->list_end = NULL; + return 0; +} diff --git a/lib/sqfs/src/misc.c b/lib/sqfs/src/misc.c new file mode 100644 index 0000000..74a4203 --- /dev/null +++ b/lib/sqfs/src/misc.c @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * misc.c + * + * Copyright (C) 2021 David Oberhollenzer + */ +#define SQFS_BUILDING_DLL +#include "config.h" + +#include "sqfs/predef.h" + +#include + +void sqfs_free(void *ptr) +{ + free(ptr); +} diff --git a/lib/sqfs/src/read_inode.c b/lib/sqfs/src/read_inode.c new file mode 100644 index 0000000..12bef48 --- /dev/null +++ b/lib/sqfs/src/read_inode.c @@ -0,0 +1,424 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * read_inode.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#define SQFS_BUILDING_DLL +#include "config.h" + +#include "sqfs/meta_reader.h" +#include "sqfs/error.h" +#include "sqfs/super.h" +#include "sqfs/inode.h" +#include "sqfs/dir.h" +#include "util/util.h" + +#include +#include +#include + +#define SWAB16(x) x = le16toh(x) +#define SWAB32(x) x = le32toh(x) +#define SWAB64(x) x = le64toh(x) + +static int set_mode(sqfs_inode_t *inode) +{ + inode->mode &= ~S_IFMT; + + switch (inode->type) { + case SQFS_INODE_SOCKET: + case SQFS_INODE_EXT_SOCKET: + inode->mode |= S_IFSOCK; + break; + case SQFS_INODE_SLINK: + case SQFS_INODE_EXT_SLINK: + inode->mode |= S_IFLNK; + break; + case SQFS_INODE_FILE: + case SQFS_INODE_EXT_FILE: + inode->mode |= S_IFREG; + break; + case SQFS_INODE_BDEV: + case SQFS_INODE_EXT_BDEV: + inode->mode |= S_IFBLK; + break; + case SQFS_INODE_DIR: + case SQFS_INODE_EXT_DIR: + inode->mode |= S_IFDIR; + break; + case SQFS_INODE_CDEV: + case SQFS_INODE_EXT_CDEV: + inode->mode |= S_IFCHR; + break; + case SQFS_INODE_FIFO: + case SQFS_INODE_EXT_FIFO: + inode->mode |= S_IFIFO; + break; + default: + return SQFS_ERROR_UNSUPPORTED; + } + + return 0; +} + +static sqfs_u64 get_block_count(sqfs_u64 size, sqfs_u64 block_size, + sqfs_u32 frag_index, sqfs_u32 frag_offset) +{ + sqfs_u64 count = size / block_size; + + if ((size % block_size) != 0 && + (frag_index == 0xFFFFFFFF || frag_offset == 0xFFFFFFFF)) { + ++count; + } + + return count; +} + +static int read_inode_file(sqfs_meta_reader_t *ir, sqfs_inode_t *base, + size_t block_size, sqfs_inode_generic_t **result) +{ + sqfs_inode_generic_t *out; + sqfs_inode_file_t file; + sqfs_u64 i, count; + int err; + + err = sqfs_meta_reader_read(ir, &file, sizeof(file)); + if (err) + return err; + + SWAB32(file.blocks_start); + SWAB32(file.fragment_index); + SWAB32(file.fragment_offset); + SWAB32(file.file_size); + + count = get_block_count(file.file_size, block_size, + file.fragment_index, file.fragment_offset); + + out = alloc_flex(sizeof(*out), sizeof(sqfs_u32), count); + if (out == NULL) + return SQFS_ERROR_ALLOC; + + out->base = *base; + out->data.file = file; + out->payload_bytes_available = count * sizeof(sqfs_u32); + out->payload_bytes_used = count * sizeof(sqfs_u32); + + err = sqfs_meta_reader_read(ir, out->extra, count * sizeof(sqfs_u32)); + if (err) { + free(out); + return err; + } + + for (i = 0; i < count; ++i) + SWAB32(out->extra[i]); + + *result = out; + return 0; +} + +static int read_inode_file_ext(sqfs_meta_reader_t *ir, sqfs_inode_t *base, + size_t block_size, sqfs_inode_generic_t **result) +{ + sqfs_inode_file_ext_t file; + sqfs_inode_generic_t *out; + sqfs_u64 i, count; + int err; + + err = sqfs_meta_reader_read(ir, &file, sizeof(file)); + if (err) + return err; + + SWAB64(file.blocks_start); + SWAB64(file.file_size); + SWAB64(file.sparse); + SWAB32(file.nlink); + SWAB32(file.fragment_idx); + SWAB32(file.fragment_offset); + SWAB32(file.xattr_idx); + + count = get_block_count(file.file_size, block_size, + file.fragment_idx, file.fragment_offset); + + out = alloc_flex(sizeof(*out), sizeof(sqfs_u32), count); + if (out == NULL) { + return errno == EOVERFLOW ? SQFS_ERROR_OVERFLOW : + SQFS_ERROR_ALLOC; + } + + out->base = *base; + out->data.file_ext = file; + out->payload_bytes_available = count * sizeof(sqfs_u32); + out->payload_bytes_used = count * sizeof(sqfs_u32); + + err = sqfs_meta_reader_read(ir, out->extra, count * sizeof(sqfs_u32)); + if (err) { + free(out); + return err; + } + + for (i = 0; i < count; ++i) + SWAB32(out->extra[i]); + + *result = out; + return 0; +} + +static int read_inode_slink(sqfs_meta_reader_t *ir, sqfs_inode_t *base, + sqfs_inode_generic_t **result) +{ + sqfs_inode_generic_t *out; + sqfs_inode_slink_t slink; + size_t size; + int err; + + err = sqfs_meta_reader_read(ir, &slink, sizeof(slink)); + if (err) + return err; + + SWAB32(slink.nlink); + SWAB32(slink.target_size); + + if (SZ_ADD_OV(slink.target_size, 1, &size) || + SZ_ADD_OV(sizeof(*out), size, &size)) { + return SQFS_ERROR_OVERFLOW; + } + + out = calloc(1, size); + if (out == NULL) + return SQFS_ERROR_ALLOC; + + out->payload_bytes_available = size - sizeof(*out); + out->payload_bytes_used = size - sizeof(*out) - 1; + out->base = *base; + out->data.slink = slink; + + err = sqfs_meta_reader_read(ir, (void *)out->extra, slink.target_size); + if (err) { + free(out); + return err; + } + + *result = out; + return 0; +} + +static int read_inode_slink_ext(sqfs_meta_reader_t *ir, sqfs_inode_t *base, + sqfs_inode_generic_t **result) +{ + sqfs_u32 xattr; + int err; + + err = read_inode_slink(ir, base, result); + if (err) + return err; + + err = sqfs_meta_reader_read(ir, &xattr, sizeof(xattr)); + if (err) { + free(*result); + return err; + } + + (*result)->data.slink_ext.xattr_idx = le32toh(xattr); + return 0; +} + +static int read_inode_dir_ext(sqfs_meta_reader_t *ir, sqfs_inode_t *base, + sqfs_inode_generic_t **result) +{ + size_t i, new_sz, index_max, index_used; + sqfs_inode_generic_t *out, *new; + sqfs_inode_dir_ext_t dir; + sqfs_dir_index_t ent; + int err; + + err = sqfs_meta_reader_read(ir, &dir, sizeof(dir)); + if (err) + return err; + + SWAB32(dir.nlink); + SWAB32(dir.size); + SWAB32(dir.start_block); + SWAB32(dir.parent_inode); + SWAB16(dir.inodex_count); + SWAB16(dir.offset); + SWAB32(dir.xattr_idx); + + index_max = dir.size ? 128 : 0; + index_used = 0; + + out = alloc_flex(sizeof(*out), 1, index_max); + if (out == NULL) + return SQFS_ERROR_ALLOC; + + out->base = *base; + out->data.dir_ext = dir; + + if (dir.size == 0) { + *result = out; + return 0; + } + + for (i = 0; i < dir.inodex_count; ++i) { + err = sqfs_meta_reader_read(ir, &ent, sizeof(ent)); + if (err) { + free(out); + return err; + } + + SWAB32(ent.start_block); + SWAB32(ent.index); + SWAB32(ent.size); + + new_sz = index_max; + while (sizeof(ent) + ent.size + 1 > new_sz - index_used) { + if (SZ_MUL_OV(new_sz, 2, &new_sz)) { + free(out); + return SQFS_ERROR_OVERFLOW; + } + } + + if (new_sz > index_max) { + new = realloc(out, sizeof(*out) + new_sz); + if (new == NULL) { + free(out); + return SQFS_ERROR_ALLOC; + } + out = new; + index_max = new_sz; + } + + memcpy((char *)out->extra + index_used, &ent, sizeof(ent)); + index_used += sizeof(ent); + + err = sqfs_meta_reader_read(ir, (char *)out->extra + index_used, + ent.size + 1); + if (err) { + free(out); + return err; + } + + index_used += ent.size + 1; + } + + out->payload_bytes_used = index_used; + out->payload_bytes_available = index_used; + *result = out; + return 0; +} + +int sqfs_meta_reader_read_inode(sqfs_meta_reader_t *ir, + const sqfs_super_t *super, + sqfs_u64 block_start, size_t offset, + sqfs_inode_generic_t **result) +{ + sqfs_inode_generic_t *out; + sqfs_inode_t inode; + int err; + + /* read base inode */ + block_start += super->inode_table_start; + + err = sqfs_meta_reader_seek(ir, block_start, offset); + if (err) + return err; + + err = sqfs_meta_reader_read(ir, &inode, sizeof(inode)); + if (err) + return err; + + SWAB16(inode.type); + SWAB16(inode.mode); + SWAB16(inode.uid_idx); + SWAB16(inode.gid_idx); + SWAB32(inode.mod_time); + SWAB32(inode.inode_number); + + err = set_mode(&inode); + if (err) + return err; + + /* inode types where the size is variable */ + switch (inode.type) { + case SQFS_INODE_FILE: + return read_inode_file(ir, &inode, super->block_size, result); + case SQFS_INODE_SLINK: + return read_inode_slink(ir, &inode, result); + case SQFS_INODE_EXT_FILE: + return read_inode_file_ext(ir, &inode, super->block_size, + result); + case SQFS_INODE_EXT_SLINK: + return read_inode_slink_ext(ir, &inode, result); + case SQFS_INODE_EXT_DIR: + return read_inode_dir_ext(ir, &inode, result); + default: + break; + } + + /* everything else */ + out = calloc(1, sizeof(*out)); + if (out == NULL) + return SQFS_ERROR_ALLOC; + + out->base = inode; + + switch (inode.type) { + case SQFS_INODE_DIR: + err = sqfs_meta_reader_read(ir, &out->data.dir, + sizeof(out->data.dir)); + if (err) + goto fail_free; + + SWAB32(out->data.dir.start_block); + SWAB32(out->data.dir.nlink); + SWAB16(out->data.dir.size); + SWAB16(out->data.dir.offset); + SWAB32(out->data.dir.parent_inode); + break; + case SQFS_INODE_BDEV: + case SQFS_INODE_CDEV: + err = sqfs_meta_reader_read(ir, &out->data.dev, + sizeof(out->data.dev)); + if (err) + goto fail_free; + SWAB32(out->data.dev.nlink); + SWAB32(out->data.dev.devno); + break; + case SQFS_INODE_FIFO: + case SQFS_INODE_SOCKET: + err = sqfs_meta_reader_read(ir, &out->data.ipc, + sizeof(out->data.ipc)); + if (err) + goto fail_free; + SWAB32(out->data.ipc.nlink); + break; + case SQFS_INODE_EXT_BDEV: + case SQFS_INODE_EXT_CDEV: + err = sqfs_meta_reader_read(ir, &out->data.dev_ext, + sizeof(out->data.dev_ext)); + if (err) + goto fail_free; + SWAB32(out->data.dev_ext.nlink); + SWAB32(out->data.dev_ext.devno); + SWAB32(out->data.dev_ext.xattr_idx); + break; + case SQFS_INODE_EXT_FIFO: + case SQFS_INODE_EXT_SOCKET: + err = sqfs_meta_reader_read(ir, &out->data.ipc_ext, + sizeof(out->data.ipc_ext)); + if (err) + goto fail_free; + SWAB32(out->data.ipc_ext.nlink); + SWAB32(out->data.ipc_ext.xattr_idx); + break; + default: + err = SQFS_ERROR_UNSUPPORTED; + goto fail_free; + } + + *result = out; + return 0; +fail_free: + free(out); + return err; +} diff --git a/lib/sqfs/src/read_super.c b/lib/sqfs/src/read_super.c new file mode 100644 index 0000000..11bc314 --- /dev/null +++ b/lib/sqfs/src/read_super.c @@ -0,0 +1,83 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * read_super.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#define SQFS_BUILDING_DLL +#include "config.h" + +#include "sqfs/super.h" +#include "sqfs/error.h" +#include "sqfs/io.h" +#include "util/util.h" + +#include + +int sqfs_super_read(sqfs_super_t *super, sqfs_file_t *file) +{ + size_t block_size = 0; + sqfs_super_t temp; + int i, ret; + + ret = file->read_at(file, 0, &temp, sizeof(temp)); + if (ret) + return ret; + + temp.magic = le32toh(temp.magic); + temp.inode_count = le32toh(temp.inode_count); + temp.modification_time = le32toh(temp.modification_time); + temp.block_size = le32toh(temp.block_size); + temp.fragment_entry_count = le32toh(temp.fragment_entry_count); + temp.compression_id = le16toh(temp.compression_id); + temp.block_log = le16toh(temp.block_log); + temp.flags = le16toh(temp.flags); + temp.id_count = le16toh(temp.id_count); + temp.version_major = le16toh(temp.version_major); + temp.version_minor = le16toh(temp.version_minor); + temp.root_inode_ref = le64toh(temp.root_inode_ref); + temp.bytes_used = le64toh(temp.bytes_used); + temp.id_table_start = le64toh(temp.id_table_start); + temp.xattr_id_table_start = le64toh(temp.xattr_id_table_start); + temp.inode_table_start = le64toh(temp.inode_table_start); + temp.directory_table_start = le64toh(temp.directory_table_start); + temp.fragment_table_start = le64toh(temp.fragment_table_start); + temp.export_table_start = le64toh(temp.export_table_start); + + if (temp.magic != SQFS_MAGIC) + return SFQS_ERROR_SUPER_MAGIC; + + if ((temp.version_major != SQFS_VERSION_MAJOR) || + (temp.version_minor != SQFS_VERSION_MINOR)) + return SFQS_ERROR_SUPER_VERSION; + + if ((temp.block_size - 1) & temp.block_size) + return SQFS_ERROR_SUPER_BLOCK_SIZE; + + if (temp.block_size < SQFS_MIN_BLOCK_SIZE) + return SQFS_ERROR_SUPER_BLOCK_SIZE; + + if (temp.block_size > SQFS_MAX_BLOCK_SIZE) + return SQFS_ERROR_SUPER_BLOCK_SIZE; + + if (temp.block_log < 12 || temp.block_log > 20) + return SQFS_ERROR_CORRUPTED; + + block_size = 1; + + for (i = 0; i < temp.block_log; ++i) + block_size <<= 1; + + if (temp.block_size != block_size) + return SQFS_ERROR_CORRUPTED; + + if (temp.compression_id < SQFS_COMP_MIN || + temp.compression_id > SQFS_COMP_MAX) + return SQFS_ERROR_UNSUPPORTED; + + if (temp.id_count == 0) + return SQFS_ERROR_CORRUPTED; + + memcpy(super, &temp, sizeof(temp)); + return 0; +} diff --git a/lib/sqfs/src/read_table.c b/lib/sqfs/src/read_table.c new file mode 100644 index 0000000..c6a9bbe --- /dev/null +++ b/lib/sqfs/src/read_table.c @@ -0,0 +1,91 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * read_table.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#define SQFS_BUILDING_DLL +#include "config.h" + +#include "sqfs/meta_reader.h" +#include "sqfs/error.h" +#include "sqfs/table.h" +#include "sqfs/block.h" +#include "sqfs/io.h" +#include "util/util.h" + +#include + +int sqfs_read_table(sqfs_file_t *file, sqfs_compressor_t *cmp, + size_t table_size, sqfs_u64 location, sqfs_u64 lower_limit, + sqfs_u64 upper_limit, void **out) +{ + size_t diff, block_count, blk_idx = 0; + sqfs_u64 start, *locations; + sqfs_meta_reader_t *m; + void *data, *ptr; + int err; + + data = malloc(table_size); + if (data == NULL) + return SQFS_ERROR_ALLOC; + + /* restore list from image */ + block_count = table_size / SQFS_META_BLOCK_SIZE; + + if ((table_size % SQFS_META_BLOCK_SIZE) != 0) + ++block_count; + + locations = alloc_array(sizeof(sqfs_u64), block_count); + + if (locations == NULL) { + err = SQFS_ERROR_ALLOC; + goto fail_data; + } + + err = file->read_at(file, location, locations, + sizeof(sqfs_u64) * block_count); + if (err) + goto fail_idx; + + /* Read the actual data */ + m = sqfs_meta_reader_create(file, cmp, lower_limit, upper_limit); + if (m == NULL) { + err = SQFS_ERROR_ALLOC; + goto fail_idx; + } + + ptr = data; + + while (table_size > 0) { + start = le64toh(locations[blk_idx++]); + + err = sqfs_meta_reader_seek(m, start, 0); + if (err) + goto fail; + + diff = SQFS_META_BLOCK_SIZE; + if (diff > table_size) + diff = table_size; + + err = sqfs_meta_reader_read(m, ptr, diff); + if (err) + goto fail; + + ptr = (char *)ptr + diff; + table_size -= diff; + } + + sqfs_drop(m); + free(locations); + *out = data; + return 0; +fail: + sqfs_drop(m); +fail_idx: + free(locations); +fail_data: + free(data); + *out = NULL; + return err; +} diff --git a/lib/sqfs/src/readdir.c b/lib/sqfs/src/readdir.c new file mode 100644 index 0000000..e2dbcd4 --- /dev/null +++ b/lib/sqfs/src/readdir.c @@ -0,0 +1,161 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * readdir.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#define SQFS_BUILDING_DLL +#include "config.h" + +#include "sqfs/meta_reader.h" +#include "sqfs/error.h" +#include "sqfs/super.h" +#include "sqfs/inode.h" +#include "sqfs/dir.h" +#include "compat.h" + +#include +#include + +int sqfs_meta_reader_read_dir_header(sqfs_meta_reader_t *m, + sqfs_dir_header_t *hdr) +{ + int err = sqfs_meta_reader_read(m, hdr, sizeof(*hdr)); + if (err) + return err; + + hdr->count = le32toh(hdr->count); + hdr->start_block = le32toh(hdr->start_block); + hdr->inode_number = le32toh(hdr->inode_number); + + if (hdr->count > (SQFS_MAX_DIR_ENT - 1)) + return SQFS_ERROR_CORRUPTED; + + return 0; +} + +int sqfs_meta_reader_read_dir_ent(sqfs_meta_reader_t *m, + sqfs_dir_entry_t **result) +{ + sqfs_dir_entry_t ent, *out; + sqfs_u16 *diff_u16; + int err; + + err = sqfs_meta_reader_read(m, &ent, sizeof(ent)); + if (err) + return err; + + diff_u16 = (sqfs_u16 *)&ent.inode_diff; + *diff_u16 = le16toh(*diff_u16); + + ent.offset = le16toh(ent.offset); + ent.type = le16toh(ent.type); + ent.size = le16toh(ent.size); + + out = calloc(1, sizeof(*out) + ent.size + 2); + if (out == NULL) + return SQFS_ERROR_ALLOC; + + *out = ent; + err = sqfs_meta_reader_read(m, out->name, ent.size + 1); + if (err) { + free(out); + return err; + } + + *result = out; + return 0; +} + +int sqfs_readdir_state_init(sqfs_readdir_state_t *s, const sqfs_super_t *super, + const sqfs_inode_generic_t *inode) +{ + memset(s, 0, sizeof(*s)); + + if (inode->base.type == SQFS_INODE_DIR) { + s->init.block = inode->data.dir.start_block; + s->init.offset = inode->data.dir.offset; + s->init.size = inode->data.dir.size; + } else if (inode->base.type == SQFS_INODE_EXT_DIR) { + s->init.block = inode->data.dir_ext.start_block; + s->init.offset = inode->data.dir_ext.offset; + s->init.size = inode->data.dir_ext.size; + } else { + return SQFS_ERROR_NOT_DIR; + } + + s->init.block += super->directory_table_start; + s->current = s->init; + return 0; +} + +int sqfs_meta_reader_readdir(sqfs_meta_reader_t *m, sqfs_readdir_state_t *it, + sqfs_dir_entry_t **ent, + sqfs_u32 *inum, sqfs_u64 *iref) +{ + size_t count; + int ret; + + if (it->entries == 0) { + sqfs_dir_header_t hdr; + + if (it->current.size <= sizeof(hdr)) + goto out_eof; + + ret = sqfs_meta_reader_seek(m, it->current.block, + it->current.offset); + if (ret != 0) + return ret; + + ret = sqfs_meta_reader_read_dir_header(m, &hdr); + if (ret != 0) + return ret; + + sqfs_meta_reader_get_position(m, &it->current.block, + &it->current.offset); + + it->current.size -= sizeof(hdr); + it->entries = hdr.count + 1; + it->inum_base = hdr.inode_number; + it->inode_block = hdr.start_block; + } + + if (it->current.size <= sizeof(**ent)) + goto out_eof; + + ret = sqfs_meta_reader_seek(m, it->current.block, it->current.offset); + if (ret != 0) + return ret; + + ret = sqfs_meta_reader_read_dir_ent(m, ent); + if (ret) + return ret; + + sqfs_meta_reader_get_position(m, &it->current.block, + &it->current.offset); + + it->current.size -= sizeof(**ent); + it->entries -= 1; + + count = (*ent)->size + 1; + + if (count >= it->current.size) { + it->current.size = 0; + } else { + it->current.size -= count; + } + + if (inum != NULL) + *inum = it->inum_base + (*ent)->inode_diff; + + if (iref != NULL) { + *iref = (sqfs_u64)it->inode_block << 16UL; + *iref |= (*ent)->offset; + } + + return 0; +out_eof: + it->current.size = 0; + it->entries = 0; + return 1; +} diff --git a/lib/sqfs/src/super.c b/lib/sqfs/src/super.c new file mode 100644 index 0000000..470c06a --- /dev/null +++ b/lib/sqfs/src/super.c @@ -0,0 +1,50 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * super.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#define SQFS_BUILDING_DLL +#include "config.h" + +#include "sqfs/super.h" +#include "sqfs/error.h" + +#include + +int sqfs_super_init(sqfs_super_t *super, size_t block_size, sqfs_u32 mtime, + SQFS_COMPRESSOR compressor) +{ + unsigned int i; + + if (block_size & (block_size - 1)) + return SQFS_ERROR_SUPER_BLOCK_SIZE; + + if (block_size < SQFS_MIN_BLOCK_SIZE) + return SQFS_ERROR_SUPER_BLOCK_SIZE; + + if (block_size > SQFS_MAX_BLOCK_SIZE) + return SQFS_ERROR_SUPER_BLOCK_SIZE; + + memset(super, 0, sizeof(*super)); + super->magic = SQFS_MAGIC; + super->modification_time = mtime; + super->block_size = block_size; + super->compression_id = compressor; + super->flags = SQFS_FLAG_NO_FRAGMENTS | SQFS_FLAG_NO_XATTRS; + super->flags |= SQFS_FLAG_NO_DUPLICATES; + super->version_major = SQFS_VERSION_MAJOR; + super->version_minor = SQFS_VERSION_MINOR; + super->bytes_used = sizeof(*super); + super->id_table_start = 0xFFFFFFFFFFFFFFFFUL; + super->xattr_id_table_start = 0xFFFFFFFFFFFFFFFFUL; + super->inode_table_start = 0xFFFFFFFFFFFFFFFFUL; + super->directory_table_start = 0xFFFFFFFFFFFFFFFFUL; + super->fragment_table_start = 0xFFFFFFFFFFFFFFFFUL; + super->export_table_start = 0xFFFFFFFFFFFFFFFFUL; + + for (i = block_size; i != 0x01; i >>= 1) + super->block_log += 1; + + return 0; +} diff --git a/lib/sqfs/src/unix/io_file.c b/lib/sqfs/src/unix/io_file.c new file mode 100644 index 0000000..e1fb9db --- /dev/null +++ b/lib/sqfs/src/unix/io_file.c @@ -0,0 +1,196 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * io_file.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#define SQFS_BUILDING_DLL +#include "config.h" + +#include "sqfs/io.h" +#include "sqfs/error.h" + +#include +#include +#include +#include +#include +#include + + +typedef struct { + sqfs_file_t base; + + bool readonly; + sqfs_u64 size; + int fd; +} sqfs_file_stdio_t; + + +static void stdio_destroy(sqfs_object_t *base) +{ + sqfs_file_stdio_t *file = (sqfs_file_stdio_t *)base; + + close(file->fd); + free(file); +} + +static sqfs_object_t *stdio_copy(const sqfs_object_t *base) +{ + const sqfs_file_stdio_t *file = (const sqfs_file_stdio_t *)base; + sqfs_file_stdio_t *copy; + int err; + + if (!file->readonly) { + errno = ENOTSUP; + return NULL; + } + + copy = calloc(1, sizeof(*copy)); + if (copy == NULL) + return NULL; + + memcpy(copy, file, sizeof(*file)); + + copy->fd = dup(file->fd); + if (copy->fd < 0) { + err = errno; + free(copy); + copy = NULL; + errno = err; + } + + return (sqfs_object_t *)copy; +} + +static int stdio_read_at(sqfs_file_t *base, sqfs_u64 offset, + void *buffer, size_t size) +{ + sqfs_file_stdio_t *file = (sqfs_file_stdio_t *)base; + ssize_t ret; + + while (size > 0) { + ret = pread(file->fd, buffer, size, offset); + + if (ret < 0) { + if (errno == EINTR) + continue; + return SQFS_ERROR_IO; + } + + if (ret == 0) + return SQFS_ERROR_OUT_OF_BOUNDS; + + buffer = (char *)buffer + ret; + size -= ret; + offset += ret; + } + + return 0; +} + +static int stdio_write_at(sqfs_file_t *base, sqfs_u64 offset, + const void *buffer, size_t size) +{ + sqfs_file_stdio_t *file = (sqfs_file_stdio_t *)base; + ssize_t ret; + + while (size > 0) { + ret = pwrite(file->fd, buffer, size, offset); + + if (ret < 0) { + if (errno == EINTR) + continue; + return SQFS_ERROR_IO; + } + + if (ret == 0) + return SQFS_ERROR_OUT_OF_BOUNDS; + + buffer = (const char *)buffer + ret; + size -= ret; + offset += ret; + } + + if (offset >= file->size) + file->size = offset; + + return 0; +} + +static sqfs_u64 stdio_get_size(const sqfs_file_t *base) +{ + const sqfs_file_stdio_t *file = (const sqfs_file_stdio_t *)base; + + return file->size; +} + +static int stdio_truncate(sqfs_file_t *base, sqfs_u64 size) +{ + sqfs_file_stdio_t *file = (sqfs_file_stdio_t *)base; + + if (ftruncate(file->fd, size)) + return SQFS_ERROR_IO; + + file->size = size; + return 0; +} + + +sqfs_file_t *sqfs_open_file(const char *filename, sqfs_u32 flags) +{ + sqfs_file_stdio_t *file; + int open_mode, temp; + sqfs_file_t *base; + struct stat sb; + + if (flags & ~SQFS_FILE_OPEN_ALL_FLAGS) { + errno = EINVAL; + return NULL; + } + + file = calloc(1, sizeof(*file)); + base = (sqfs_file_t *)file; + if (file == NULL) + return NULL; + + sqfs_object_init(file, stdio_destroy, stdio_copy); + + if (flags & SQFS_FILE_OPEN_READ_ONLY) { + file->readonly = true; + open_mode = O_RDONLY; + } else { + file->readonly = false; + open_mode = O_CREAT | O_RDWR; + + if (flags & SQFS_FILE_OPEN_OVERWRITE) { + open_mode |= O_TRUNC; + } else { + open_mode |= O_EXCL; + } + } + + file->fd = open(filename, open_mode, 0644); + if (file->fd < 0) { + temp = errno; + free(file); + errno = temp; + return NULL; + } + + if (fstat(file->fd, &sb)) { + temp = errno; + close(file->fd); + free(file); + errno = temp; + return NULL; + } + + file->size = sb.st_size; + + base->read_at = stdio_read_at; + base->write_at = stdio_write_at; + base->get_size = stdio_get_size; + base->truncate = stdio_truncate; + return base; +} diff --git a/lib/sqfs/src/win32/io_file.c b/lib/sqfs/src/win32/io_file.c new file mode 100644 index 0000000..548a246 --- /dev/null +++ b/lib/sqfs/src/win32/io_file.c @@ -0,0 +1,237 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * io_file.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#define SQFS_BUILDING_DLL +#include "config.h" + +#include "sqfs/io.h" +#include "sqfs/error.h" + +#include + +#define WIN32_LEAN_AND_MEAN +#include + + +typedef struct { + sqfs_file_t base; + + bool readonly; + sqfs_u64 size; + HANDLE fd; +} sqfs_file_stdio_t; + + +static void stdio_destroy(sqfs_object_t *base) +{ + sqfs_file_stdio_t *file = (sqfs_file_stdio_t *)base; + + CloseHandle(file->fd); + free(file); +} + +static sqfs_object_t *stdio_copy(const sqfs_object_t *base) +{ + const sqfs_file_stdio_t *file = (const sqfs_file_stdio_t *)base; + sqfs_file_stdio_t *copy; + BOOL ret; + + if (!file->readonly) { + SetLastError(ERROR_NOT_SUPPORTED); + return NULL; + } + + copy = calloc(1, sizeof(*copy)); + if (copy == NULL) + return NULL; + + memcpy(copy, file, sizeof(*file)); + + ret = DuplicateHandle(GetCurrentProcess(), file->fd, + GetCurrentProcess(), ©->fd, + 0, FALSE, DUPLICATE_SAME_ACCESS); + + if (!ret) { + free(copy); + return NULL; + } + + return (sqfs_object_t *)copy; +} + +static int stdio_read_at(sqfs_file_t *base, sqfs_u64 offset, + void *buffer, size_t size) +{ + sqfs_file_stdio_t *file = (sqfs_file_stdio_t *)base; + DWORD actually_read; + LARGE_INTEGER pos; + + if (offset >= file->size) + return SQFS_ERROR_OUT_OF_BOUNDS; + + if (size == 0) + return 0; + + if ((offset + size - 1) >= file->size) + return SQFS_ERROR_OUT_OF_BOUNDS; + + pos.QuadPart = offset; + + if (!SetFilePointerEx(file->fd, pos, NULL, FILE_BEGIN)) + return SQFS_ERROR_IO; + + while (size > 0) { + if (!ReadFile(file->fd, buffer, size, &actually_read, NULL)) + return SQFS_ERROR_IO; + + size -= actually_read; + buffer = (char *)buffer + actually_read; + } + + return 0; +} + +static int stdio_write_at(sqfs_file_t *base, sqfs_u64 offset, + const void *buffer, size_t size) +{ + sqfs_file_stdio_t *file = (sqfs_file_stdio_t *)base; + DWORD actually_read; + LARGE_INTEGER pos; + + if (size == 0) + return 0; + + pos.QuadPart = offset; + + if (!SetFilePointerEx(file->fd, pos, NULL, FILE_BEGIN)) + return SQFS_ERROR_IO; + + while (size > 0) { + if (!WriteFile(file->fd, buffer, size, &actually_read, NULL)) + return SQFS_ERROR_IO; + + size -= actually_read; + buffer = (char *)buffer + actually_read; + offset += actually_read; + + if (offset > file->size) + file->size = offset; + } + + return 0; +} + +static sqfs_u64 stdio_get_size(const sqfs_file_t *base) +{ + const sqfs_file_stdio_t *file = (const sqfs_file_stdio_t *)base; + + return file->size; +} + +static int stdio_truncate(sqfs_file_t *base, sqfs_u64 size) +{ + sqfs_file_stdio_t *file = (sqfs_file_stdio_t *)base; + LARGE_INTEGER pos; + + pos.QuadPart = size; + + if (!SetFilePointerEx(file->fd, pos, NULL, FILE_BEGIN)) + return SQFS_ERROR_IO; + + if (!SetEndOfFile(file->fd)) + return SQFS_ERROR_IO; + + file->size = size; + return 0; +} + + +sqfs_file_t *sqfs_open_file(const char *filename, sqfs_u32 flags) +{ + int access_flags, creation_mode, share_mode; + sqfs_file_stdio_t *file; + LARGE_INTEGER size; + sqfs_file_t *base; + WCHAR *wpath = NULL; + DWORD length; + + if (flags & ~SQFS_FILE_OPEN_ALL_FLAGS) { + SetLastError(ERROR_INVALID_PARAMETER); + return NULL; + } + + if (!(flags & SQFS_FILE_OPEN_NO_CHARSET_XFRM)) { + length = MultiByteToWideChar(CP_UTF8, 0, filename, -1, NULL, 0); + if (length <= 0) + return NULL; + + wpath = calloc(sizeof(wpath[0]), length + 1); + if (wpath == NULL) { + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + return NULL; + } + + MultiByteToWideChar(CP_UTF8, 0, filename, -1, + wpath, length + 1); + wpath[length] = '\0'; + } + + file = calloc(1, sizeof(*file)); + base = (sqfs_file_t *)file; + if (file == NULL) { + free(wpath); + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + return NULL; + } + + sqfs_object_init(file, stdio_destroy, stdio_copy); + + if (flags & SQFS_FILE_OPEN_READ_ONLY) { + file->readonly = true; + access_flags = GENERIC_READ; + creation_mode = OPEN_EXISTING; + share_mode = FILE_SHARE_READ; + } else { + file->readonly = false; + access_flags = GENERIC_READ | GENERIC_WRITE; + share_mode = 0; + + if (flags & SQFS_FILE_OPEN_OVERWRITE) { + creation_mode = CREATE_ALWAYS; + } else { + creation_mode = CREATE_NEW; + } + } + + if (flags & SQFS_FILE_OPEN_NO_CHARSET_XFRM) { + file->fd = CreateFileA(filename, access_flags, share_mode, NULL, + creation_mode, FILE_ATTRIBUTE_NORMAL, + NULL); + } else { + file->fd = CreateFileW(wpath, access_flags, share_mode, NULL, + creation_mode, FILE_ATTRIBUTE_NORMAL, + NULL); + } + + free(wpath); + + if (file->fd == INVALID_HANDLE_VALUE) { + free(file); + return NULL; + } + + if (!GetFileSizeEx(file->fd, &size)) { + free(file); + return NULL; + } + + file->size = size.QuadPart; + base->read_at = stdio_read_at; + base->write_at = stdio_write_at; + base->get_size = stdio_get_size; + base->truncate = stdio_truncate; + return base; +} diff --git a/lib/sqfs/src/write_inode.c b/lib/sqfs/src/write_inode.c new file mode 100644 index 0000000..118b713 --- /dev/null +++ b/lib/sqfs/src/write_inode.c @@ -0,0 +1,220 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * write_inode.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#define SQFS_BUILDING_DLL +#include "config.h" + +#include "sqfs/meta_writer.h" +#include "sqfs/error.h" +#include "sqfs/inode.h" +#include "sqfs/dir.h" +#include "compat.h" + +#include +#include + +#if defined(_WIN32) || defined(__WINDOWS__) +# include +# ifdef _MSC_VER +# define alloca _alloca +# endif +#elif defined(HAVE_ALLOCA_H) +# include +#endif + +static int write_block_sizes(sqfs_meta_writer_t *ir, + const sqfs_inode_generic_t *n) +{ + sqfs_u32 *sizes; + size_t i; + + if (n->payload_bytes_used < sizeof(sizes[0])) + return 0; + + if ((n->payload_bytes_used % sizeof(sizes[0])) != 0) + return SQFS_ERROR_CORRUPTED; + + sizes = alloca(n->payload_bytes_used); + + for (i = 0; i < (n->payload_bytes_used / sizeof(sizes[0])); ++i) + sizes[i] = htole32(n->extra[i]); + + return sqfs_meta_writer_append(ir, sizes, n->payload_bytes_used); +} + +static int write_dir_index(sqfs_meta_writer_t *ir, const sqfs_u8 *data, + size_t count) +{ + sqfs_dir_index_t ent; + size_t len; + int err; + + while (count > sizeof(ent)) { + memcpy(&ent, data, sizeof(ent)); + data += sizeof(ent); + count -= sizeof(ent); + len = ent.size + 1; + + if (len > count) + return SQFS_ERROR_CORRUPTED; + + ent.start_block = htole32(ent.start_block); + ent.index = htole32(ent.index); + ent.size = htole32(ent.size); + + err = sqfs_meta_writer_append(ir, &ent, sizeof(ent)); + if (err) + return err; + + err = sqfs_meta_writer_append(ir, data, len); + if (err) + return err; + + data += len; + count -= len; + } + + return 0; +} + +int sqfs_meta_writer_write_inode(sqfs_meta_writer_t *ir, + const sqfs_inode_generic_t *n) +{ + sqfs_inode_t base; + int ret; + + base.type = htole16(n->base.type); + base.mode = htole16(n->base.mode & ~SQFS_INODE_MODE_MASK); + base.uid_idx = htole16(n->base.uid_idx); + base.gid_idx = htole16(n->base.gid_idx); + base.mod_time = htole32(n->base.mod_time); + base.inode_number = htole32(n->base.inode_number); + + ret = sqfs_meta_writer_append(ir, &base, sizeof(base)); + if (ret) + return ret; + + switch (n->base.type) { + case SQFS_INODE_DIR: { + sqfs_inode_dir_t dir = { + .start_block = htole32(n->data.dir.start_block), + .nlink = htole32(n->data.dir.nlink), + .size = htole16(n->data.dir.size), + .offset = htole16(n->data.dir.offset), + .parent_inode = htole32(n->data.dir.parent_inode), + }; + return sqfs_meta_writer_append(ir, &dir, sizeof(dir)); + } + case SQFS_INODE_FILE: { + sqfs_inode_file_t file = { + .blocks_start = htole32(n->data.file.blocks_start), + .fragment_index = htole32(n->data.file.fragment_index), + .fragment_offset = + htole32(n->data.file.fragment_offset), + .file_size = htole32(n->data.file.file_size), + }; + ret = sqfs_meta_writer_append(ir, &file, sizeof(file)); + if (ret) + return ret; + return write_block_sizes(ir, n); + } + case SQFS_INODE_SLINK: { + sqfs_inode_slink_t slink = { + .nlink = htole32(n->data.slink.nlink), + .target_size = htole32(n->data.slink.target_size), + }; + ret = sqfs_meta_writer_append(ir, &slink, sizeof(slink)); + if (ret) + return ret; + return sqfs_meta_writer_append(ir, n->extra, + n->data.slink.target_size); + } + case SQFS_INODE_BDEV: + case SQFS_INODE_CDEV: { + sqfs_inode_dev_t dev = { + .nlink = htole32(n->data.dev.nlink), + .devno = htole32(n->data.dev.devno), + }; + return sqfs_meta_writer_append(ir, &dev, sizeof(dev)); + } + case SQFS_INODE_FIFO: + case SQFS_INODE_SOCKET: { + sqfs_inode_ipc_t ipc = { + .nlink = htole32(n->data.ipc.nlink), + }; + return sqfs_meta_writer_append(ir, &ipc, sizeof(ipc)); + } + case SQFS_INODE_EXT_DIR: { + sqfs_inode_dir_ext_t dir = { + .nlink = htole32(n->data.dir_ext.nlink), + .size = htole32(n->data.dir_ext.size), + .start_block = htole32(n->data.dir_ext.start_block), + .parent_inode = htole32(n->data.dir_ext.parent_inode), + .inodex_count = htole16(n->data.dir_ext.inodex_count), + .offset = htole16(n->data.dir_ext.offset), + .xattr_idx = htole32(n->data.dir_ext.xattr_idx), + }; + ret = sqfs_meta_writer_append(ir, &dir, sizeof(dir)); + if (ret) + return ret; + return write_dir_index(ir, (const sqfs_u8 *)n->extra, + n->payload_bytes_used); + } + case SQFS_INODE_EXT_FILE: { + sqfs_inode_file_ext_t file = { + .blocks_start = htole64(n->data.file_ext.blocks_start), + .file_size = htole64(n->data.file_ext.file_size), + .sparse = htole64(n->data.file_ext.sparse), + .nlink = htole32(n->data.file_ext.nlink), + .fragment_idx = htole32(n->data.file_ext.fragment_idx), + .fragment_offset = + htole32(n->data.file_ext.fragment_offset), + .xattr_idx = htole32(n->data.file_ext.xattr_idx), + }; + ret = sqfs_meta_writer_append(ir, &file, sizeof(file)); + if (ret) + return ret; + return write_block_sizes(ir, n); + } + case SQFS_INODE_EXT_SLINK: { + sqfs_inode_slink_t slink = { + .nlink = htole32(n->data.slink_ext.nlink), + .target_size = htole32(n->data.slink_ext.target_size), + }; + sqfs_u32 xattr = htole32(n->data.slink_ext.xattr_idx); + + ret = sqfs_meta_writer_append(ir, &slink, sizeof(slink)); + if (ret) + return ret; + ret = sqfs_meta_writer_append(ir, n->extra, + n->data.slink_ext.target_size); + if (ret) + return ret; + return sqfs_meta_writer_append(ir, &xattr, sizeof(xattr)); + } + case SQFS_INODE_EXT_BDEV: + case SQFS_INODE_EXT_CDEV: { + sqfs_inode_dev_ext_t dev = { + .nlink = htole32(n->data.dev_ext.nlink), + .devno = htole32(n->data.dev_ext.devno), + .xattr_idx = htole32(n->data.dev_ext.xattr_idx), + }; + return sqfs_meta_writer_append(ir, &dev, sizeof(dev)); + } + case SQFS_INODE_EXT_FIFO: + case SQFS_INODE_EXT_SOCKET: { + sqfs_inode_ipc_ext_t ipc = { + .nlink = htole32(n->data.ipc_ext.nlink), + .xattr_idx = htole32(n->data.ipc_ext.xattr_idx), + }; + return sqfs_meta_writer_append(ir, &ipc, sizeof(ipc)); + } + default: + break; + } + + return SQFS_ERROR_UNSUPPORTED; +} diff --git a/lib/sqfs/src/write_super.c b/lib/sqfs/src/write_super.c new file mode 100644 index 0000000..35127da --- /dev/null +++ b/lib/sqfs/src/write_super.c @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * write_super.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#define SQFS_BUILDING_DLL +#include "config.h" + +#include "sqfs/super.h" +#include "sqfs/io.h" +#include "compat.h" + +int sqfs_super_write(const sqfs_super_t *super, sqfs_file_t *file) +{ + sqfs_super_t copy; + + copy.magic = htole32(super->magic); + copy.inode_count = htole32(super->inode_count); + copy.modification_time = htole32(super->modification_time); + copy.block_size = htole32(super->block_size); + copy.fragment_entry_count = htole32(super->fragment_entry_count); + copy.compression_id = htole16(super->compression_id); + copy.block_log = htole16(super->block_log); + copy.flags = htole16(super->flags); + copy.id_count = htole16(super->id_count); + copy.version_major = htole16(super->version_major); + copy.version_minor = htole16(super->version_minor); + copy.root_inode_ref = htole64(super->root_inode_ref); + copy.bytes_used = htole64(super->bytes_used); + copy.id_table_start = htole64(super->id_table_start); + copy.xattr_id_table_start = htole64(super->xattr_id_table_start); + copy.inode_table_start = htole64(super->inode_table_start); + copy.directory_table_start = htole64(super->directory_table_start); + copy.fragment_table_start = htole64(super->fragment_table_start); + copy.export_table_start = htole64(super->export_table_start); + + return file->write_at(file, 0, ©, sizeof(copy)); +} diff --git a/lib/sqfs/src/write_table.c b/lib/sqfs/src/write_table.c new file mode 100644 index 0000000..6f28a75 --- /dev/null +++ b/lib/sqfs/src/write_table.c @@ -0,0 +1,81 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * write_table.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#define SQFS_BUILDING_DLL +#include "config.h" + +#include "sqfs/meta_writer.h" +#include "sqfs/error.h" +#include "sqfs/super.h" +#include "sqfs/table.h" +#include "sqfs/block.h" +#include "sqfs/io.h" +#include "util/util.h" + +#include + +int sqfs_write_table(sqfs_file_t *file, sqfs_compressor_t *cmp, + const void *data, size_t table_size, sqfs_u64 *start) +{ + size_t block_count, list_size, diff, blkidx = 0; + sqfs_u64 off, *locations; + sqfs_meta_writer_t *m; + int ret; + + block_count = table_size / SQFS_META_BLOCK_SIZE; + if ((table_size % SQFS_META_BLOCK_SIZE) != 0) + ++block_count; + + locations = alloc_array(sizeof(sqfs_u64), block_count); + + if (locations == NULL) + return SQFS_ERROR_ALLOC; + + /* Write actual data */ + m = sqfs_meta_writer_create(file, cmp, 0); + if (m == NULL) { + ret = SQFS_ERROR_ALLOC; + goto out_idx; + } + + while (table_size > 0) { + locations[blkidx++] = htole64(file->get_size(file)); + + diff = SQFS_META_BLOCK_SIZE; + if (diff > table_size) + diff = table_size; + + ret = sqfs_meta_writer_append(m, data, diff); + if (ret) + goto out; + + data = (const char *)data + diff; + table_size -= diff; + } + + ret = sqfs_meta_writer_flush(m); + if (ret) + goto out; + + /* write location list */ + *start = file->get_size(file); + + list_size = sizeof(sqfs_u64) * block_count; + + off = file->get_size(file); + + ret = file->write_at(file, off, locations, list_size); + if (ret) + goto out; + + /* cleanup */ + ret = 0; +out: + sqfs_drop(m); +out_idx: + free(locations); + return ret; +} diff --git a/lib/sqfs/src/xattr/xattr.c b/lib/sqfs/src/xattr/xattr.c new file mode 100644 index 0000000..29ecebf --- /dev/null +++ b/lib/sqfs/src/xattr/xattr.c @@ -0,0 +1,49 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * write_xattr.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#define SQFS_BUILDING_DLL +#include "config.h" +#include "sqfs/xattr.h" +#include "sqfs/error.h" + +#include + +static const struct { + const char *prefix; + SQFS_XATTR_TYPE type; +} xattr_types[] = { + { "user.", SQFS_XATTR_USER }, + { "trusted.", SQFS_XATTR_TRUSTED }, + { "security.", SQFS_XATTR_SECURITY }, +}; + +int sqfs_get_xattr_prefix_id(const char *key) +{ + size_t i, len; + + for (i = 0; i < sizeof(xattr_types) / sizeof(xattr_types[0]); ++i) { + len = strlen(xattr_types[i].prefix); + + if (strncmp(key, xattr_types[i].prefix, len) == 0 && + strlen(key) > len) { + return xattr_types[i].type; + } + } + + return SQFS_ERROR_UNSUPPORTED; +} + +const char *sqfs_get_xattr_prefix(SQFS_XATTR_TYPE id) +{ + size_t i; + + for (i = 0; i < sizeof(xattr_types) / sizeof(xattr_types[0]); ++i) { + if (xattr_types[i].type == id) + return xattr_types[i].prefix; + } + + return NULL; +} diff --git a/lib/sqfs/src/xattr/xattr_reader.c b/lib/sqfs/src/xattr/xattr_reader.c new file mode 100644 index 0000000..9e3ea76 --- /dev/null +++ b/lib/sqfs/src/xattr/xattr_reader.c @@ -0,0 +1,336 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * xattr_reader.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#define SQFS_BUILDING_DLL +#include "config.h" + +#include "sqfs/xattr_reader.h" +#include "sqfs/meta_reader.h" +#include "sqfs/super.h" +#include "sqfs/xattr.h" +#include "sqfs/error.h" +#include "sqfs/block.h" +#include "sqfs/io.h" +#include "util/util.h" + +#include +#include +#include + +struct sqfs_xattr_reader_t { + sqfs_object_t base; + + sqfs_u64 xattr_start; + sqfs_u64 xattr_end; + + size_t num_id_blocks; + size_t num_ids; + + sqfs_u64 *id_block_starts; + + sqfs_meta_reader_t *idrd; + sqfs_meta_reader_t *kvrd; +}; + +static sqfs_object_t *xattr_reader_copy(const sqfs_object_t *obj) +{ + const sqfs_xattr_reader_t *xr = (const sqfs_xattr_reader_t *)obj; + sqfs_xattr_reader_t *copy = malloc(sizeof(*copy)); + + if (copy == NULL) + return NULL; + + memcpy(copy, xr, sizeof(*xr)); + + if (xr->kvrd != NULL) { + copy->kvrd = sqfs_copy(xr->kvrd); + if (copy->kvrd == NULL) + goto fail; + } + + if (xr->idrd != NULL) { + copy->idrd = sqfs_copy(xr->idrd); + if (copy->idrd == NULL) + goto fail; + } + + if (xr->id_block_starts != NULL) { + copy->id_block_starts = alloc_array(sizeof(sqfs_u64), + xr->num_id_blocks); + if (copy->id_block_starts == NULL) + goto fail; + + memcpy(copy->id_block_starts, xr->id_block_starts, + sizeof(sqfs_u64) * xr->num_id_blocks); + } + + return (sqfs_object_t *)copy; +fail: + sqfs_drop(copy->idrd); + sqfs_drop(copy->kvrd); + free(copy); + return NULL; +} + +static void xattr_reader_destroy(sqfs_object_t *obj) +{ + sqfs_xattr_reader_t *xr = (sqfs_xattr_reader_t *)obj; + + sqfs_drop(xr->kvrd); + sqfs_drop(xr->idrd); + free(xr->id_block_starts); + free(xr); +} + +int sqfs_xattr_reader_load(sqfs_xattr_reader_t *xr, const sqfs_super_t *super, + sqfs_file_t *file, sqfs_compressor_t *cmp) +{ + sqfs_xattr_id_table_t idtbl; + size_t i; + int err; + + /* sanity check */ + if (super->flags & SQFS_FLAG_NO_XATTRS) + return 0; + + if (super->xattr_id_table_start == 0xFFFFFFFFFFFFFFFF) + return 0; + + if (super->xattr_id_table_start >= super->bytes_used) + return SQFS_ERROR_OUT_OF_BOUNDS; + + /* cleanup pre-existing data */ + xr->idrd = sqfs_drop(xr->idrd); + xr->kvrd = sqfs_drop(xr->kvrd); + + free(xr->id_block_starts); + xr->id_block_starts = NULL; + + /* read the locations table */ + err = file->read_at(file, super->xattr_id_table_start, + &idtbl, sizeof(idtbl)); + if (err) + return err; + + xr->xattr_start = le64toh(idtbl.xattr_table_start); + xr->num_ids = le32toh(idtbl.xattr_ids); + xr->num_id_blocks = + (xr->num_ids * sizeof(sqfs_xattr_id_t)) / SQFS_META_BLOCK_SIZE; + + if ((xr->num_ids * sizeof(sqfs_xattr_id_t)) % SQFS_META_BLOCK_SIZE) + xr->num_id_blocks += 1; + + xr->id_block_starts = alloc_array(sizeof(sqfs_u64), xr->num_id_blocks); + if (xr->id_block_starts == NULL) { + if (errno == EOVERFLOW) + return SQFS_ERROR_OVERFLOW; + return SQFS_ERROR_ALLOC; + } + + err = file->read_at(file, super->xattr_id_table_start + sizeof(idtbl), + xr->id_block_starts, + sizeof(sqfs_u64) * xr->num_id_blocks); + if (err) + goto fail_blocks; + + for (i = 0; i < xr->num_id_blocks; ++i) { + xr->id_block_starts[i] = le64toh(xr->id_block_starts[i]); + + if (xr->id_block_starts[i] > super->bytes_used) { + err = SQFS_ERROR_OUT_OF_BOUNDS; + goto fail_blocks; + } + } + + /* create the meta data readers */ + xr->idrd = sqfs_meta_reader_create(file, cmp, super->id_table_start, + super->bytes_used); + if (xr->idrd == NULL) + goto fail_blocks; + + xr->kvrd = sqfs_meta_reader_create(file, cmp, super->id_table_start, + super->bytes_used); + if (xr->kvrd == NULL) + goto fail_idrd; + + xr->xattr_end = super->bytes_used; + return 0; +fail_idrd: + xr->idrd = sqfs_drop(xr->idrd); +fail_blocks: + free(xr->id_block_starts); + xr->id_block_starts = NULL; + return err; +} + +int sqfs_xattr_reader_read_key(sqfs_xattr_reader_t *xr, + sqfs_xattr_entry_t **key_out) +{ + sqfs_xattr_entry_t key, *out; + const char *prefix; + size_t plen, total; + int ret; + + ret = sqfs_meta_reader_read(xr->kvrd, &key, sizeof(key)); + if (ret) + return ret; + + key.type = le16toh(key.type); + key.size = le16toh(key.size); + + prefix = sqfs_get_xattr_prefix(key.type & SQFS_XATTR_PREFIX_MASK); + if (prefix == NULL) + return SQFS_ERROR_UNSUPPORTED; + + plen = strlen(prefix); + + if (SZ_ADD_OV(plen, key.size, &total) || SZ_ADD_OV(total, 1, &total) || + SZ_ADD_OV(sizeof(*out), total, &total)) { + return SQFS_ERROR_OVERFLOW; + } + + out = calloc(1, total); + if (out == NULL) + return SQFS_ERROR_ALLOC; + + *out = key; + memcpy(out->key, prefix, plen); + + ret = sqfs_meta_reader_read(xr->kvrd, out->key + plen, key.size); + if (ret) { + free(out); + return ret; + } + + *key_out = out; + return 0; +} + +int sqfs_xattr_reader_read_value(sqfs_xattr_reader_t *xr, + const sqfs_xattr_entry_t *key, + sqfs_xattr_value_t **val_out) +{ + size_t offset, new_offset, size; + sqfs_xattr_value_t value, *out; + sqfs_u64 ref, start, new_start; + int ret; + + ret = sqfs_meta_reader_read(xr->kvrd, &value, sizeof(value)); + if (ret) + return ret; + + if (key->type & SQFS_XATTR_FLAG_OOL) { + ret = sqfs_meta_reader_read(xr->kvrd, &ref, sizeof(ref)); + if (ret) + return ret; + + sqfs_meta_reader_get_position(xr->kvrd, &start, &offset); + + new_start = xr->xattr_start + (ref >> 16); + if (new_start >= xr->xattr_end) + return SQFS_ERROR_OUT_OF_BOUNDS; + + new_offset = ref & 0xFFFF; + if (new_offset >= SQFS_META_BLOCK_SIZE) + return SQFS_ERROR_OUT_OF_BOUNDS; + + ret = sqfs_meta_reader_seek(xr->kvrd, new_start, new_offset); + if (ret) + return ret; + + ret = sqfs_meta_reader_read(xr->kvrd, &value, sizeof(value)); + if (ret) + return ret; + } + + value.size = le32toh(value.size); + + if (SZ_ADD_OV(sizeof(*out), value.size, &size) || + SZ_ADD_OV(size, 1, &size)) { + return SQFS_ERROR_OVERFLOW; + } + + out = calloc(1, size); + if (out == NULL) + return SQFS_ERROR_ALLOC; + + *out = value; + + ret = sqfs_meta_reader_read(xr->kvrd, out->value, value.size); + if (ret) + goto fail; + + if (key->type & SQFS_XATTR_FLAG_OOL) { + ret = sqfs_meta_reader_seek(xr->kvrd, start, offset); + if (ret) + goto fail; + } + + *val_out = out; + return 0; +fail: + free(out); + return ret; +} + +int sqfs_xattr_reader_seek_kv(sqfs_xattr_reader_t *xr, + const sqfs_xattr_id_t *desc) +{ + sqfs_u32 offset = desc->xattr & 0xFFFF; + sqfs_u64 block = xr->xattr_start + (desc->xattr >> 16); + + return sqfs_meta_reader_seek(xr->kvrd, block, offset); +} + +int sqfs_xattr_reader_get_desc(sqfs_xattr_reader_t *xr, sqfs_u32 idx, + sqfs_xattr_id_t *desc) +{ + size_t block, offset; + int ret; + + memset(desc, 0, sizeof(*desc)); + + if (idx == 0xFFFFFFFF) + return 0; + + if (xr->kvrd == NULL || xr->idrd == NULL) + return idx == 0 ? 0 : SQFS_ERROR_OUT_OF_BOUNDS; + + if (idx >= xr->num_ids) + return SQFS_ERROR_OUT_OF_BOUNDS; + + offset = (idx * sizeof(*desc)) % SQFS_META_BLOCK_SIZE; + block = (idx * sizeof(*desc)) / SQFS_META_BLOCK_SIZE; + + ret = sqfs_meta_reader_seek(xr->idrd, xr->id_block_starts[block], + offset); + if (ret) + return ret; + + ret = sqfs_meta_reader_read(xr->idrd, desc, sizeof(*desc)); + if (ret) + return ret; + + desc->xattr = le64toh(desc->xattr); + desc->count = le32toh(desc->count); + desc->size = le32toh(desc->size); + return 0; +} + +sqfs_xattr_reader_t *sqfs_xattr_reader_create(sqfs_u32 flags) +{ + sqfs_xattr_reader_t *xr; + + if (flags != 0) + return NULL; + + xr = calloc(1, sizeof(*xr)); + if (xr == NULL) + return NULL; + + sqfs_object_init(xr, xattr_reader_destroy, xattr_reader_copy); + return xr; +} diff --git a/lib/sqfs/src/xattr/xattr_writer.c b/lib/sqfs/src/xattr/xattr_writer.c new file mode 100644 index 0000000..39e1b05 --- /dev/null +++ b/lib/sqfs/src/xattr/xattr_writer.c @@ -0,0 +1,127 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * xattr_writer.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "xattr_writer.h" + +static sqfs_object_t *xattr_writer_copy(const sqfs_object_t *obj) +{ + const sqfs_xattr_writer_t *xwr = (const sqfs_xattr_writer_t *)obj; + sqfs_xattr_writer_t *copy; + kv_block_desc_t *it; + + copy = calloc(1, sizeof(*copy)); + if (copy == NULL) + return NULL; + + memcpy(copy, xwr, sizeof(*xwr)); + + if (str_table_copy(©->keys, &xwr->keys)) + goto fail_keys; + + if (str_table_copy(©->values, &xwr->values)) + goto fail_values; + + if (array_init_copy(©->kv_pairs, &xwr->kv_pairs)) + goto fail_pairs; + + if (rbtree_copy(&xwr->kv_block_tree, ©->kv_block_tree) != 0) + goto fail_tree; + + for (it = xwr->kv_block_first; it != NULL; it = it->next) { + rbtree_node_t *n = rbtree_lookup(©->kv_block_tree, it); + + if (copy->kv_block_last == NULL) { + copy->kv_block_first = rbtree_node_key(n); + copy->kv_block_last = copy->kv_block_first; + } else { + copy->kv_block_last->next = rbtree_node_key(n); + copy->kv_block_last = copy->kv_block_last->next; + } + + copy->kv_block_last->next = NULL; + } + + return (sqfs_object_t *)copy; +fail_tree: + array_cleanup(©->kv_pairs); +fail_pairs: + str_table_cleanup(©->values); +fail_values: + str_table_cleanup(©->keys); +fail_keys: + free(copy); + return NULL; +} + +static void xattr_writer_destroy(sqfs_object_t *obj) +{ + sqfs_xattr_writer_t *xwr = (sqfs_xattr_writer_t *)obj; + + rbtree_cleanup(&xwr->kv_block_tree); + array_cleanup(&xwr->kv_pairs); + str_table_cleanup(&xwr->values); + str_table_cleanup(&xwr->keys); + free(xwr); +} + +static int block_compare(const void *context, + const void *lhs, const void *rhs) +{ + const sqfs_xattr_writer_t *xwr = context; + const kv_block_desc_t *l = lhs, *r = rhs; + + if (l->count != r->count) + return l->count < r->count ? -1 : 1; + + if (l->start == r->start) + return 0; + + return memcmp((sqfs_u64 *)xwr->kv_pairs.data + l->start, + (sqfs_u64 *)xwr->kv_pairs.data + r->start, + l->count * xwr->kv_pairs.size); +} + +sqfs_xattr_writer_t *sqfs_xattr_writer_create(sqfs_u32 flags) +{ + sqfs_xattr_writer_t *xwr; + + if (flags != 0) + return NULL; + + xwr = calloc(1, sizeof(*xwr)); + if (xwr == NULL) + return NULL; + + sqfs_object_init(xwr, xattr_writer_destroy, xattr_writer_copy); + + if (str_table_init(&xwr->keys)) + goto fail_keys; + + if (str_table_init(&xwr->values)) + goto fail_values; + + if (array_init(&xwr->kv_pairs, sizeof(sqfs_u64), + XATTR_INITIAL_PAIR_CAP)) { + goto fail_pairs; + } + + if (rbtree_init(&xwr->kv_block_tree, sizeof(kv_block_desc_t), + sizeof(sqfs_u32), block_compare)) { + goto fail_tree; + } + + xwr->kv_block_tree.key_context = xwr; + return xwr; +fail_tree: + array_cleanup(&xwr->kv_pairs); +fail_pairs: + str_table_cleanup(&xwr->values); +fail_values: + str_table_cleanup(&xwr->keys); +fail_keys: + free(xwr); + return NULL; +} diff --git a/lib/sqfs/src/xattr/xattr_writer.h b/lib/sqfs/src/xattr/xattr_writer.h new file mode 100644 index 0000000..792cfae --- /dev/null +++ b/lib/sqfs/src/xattr/xattr_writer.h @@ -0,0 +1,63 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * xattr_writer.h + * + * Copyright (C) 2019 David Oberhollenzer + */ +#ifndef XATTR_WRITER_H +#define XATTR_WRITER_H + +#define SQFS_BUILDING_DLL +#include "config.h" + +#include "sqfs/xattr_writer.h" +#include "sqfs/meta_writer.h" +#include "sqfs/super.h" +#include "sqfs/xattr.h" +#include "sqfs/error.h" +#include "sqfs/block.h" +#include "sqfs/io.h" + +#include "util/str_table.h" +#include "util/rbtree.h" +#include "util/array.h" +#include "util/util.h" + +#include +#include +#include + + +#define XATTR_INITIAL_PAIR_CAP 128 + +#define MK_PAIR(key, value) (((sqfs_u64)(key) << 32UL) | (sqfs_u64)(value)) +#define GET_KEY(pair) ((pair >> 32UL) & 0x0FFFFFFFFUL) +#define GET_VALUE(pair) (pair & 0x0FFFFFFFFUL) + + +typedef struct kv_block_desc_t { + struct kv_block_desc_t *next; + size_t start; + size_t count; + + sqfs_u64 start_ref; + size_t size_bytes; +} kv_block_desc_t; + +struct sqfs_xattr_writer_t { + sqfs_object_t base; + + str_table_t keys; + str_table_t values; + + array_t kv_pairs; + + size_t kv_start; + + rbtree_t kv_block_tree; + kv_block_desc_t *kv_block_first; + kv_block_desc_t *kv_block_last; + size_t num_blocks; +}; + +#endif /* XATTR_WRITER_H */ diff --git a/lib/sqfs/src/xattr/xattr_writer_flush.c b/lib/sqfs/src/xattr/xattr_writer_flush.c new file mode 100644 index 0000000..a06463f --- /dev/null +++ b/lib/sqfs/src/xattr/xattr_writer_flush.c @@ -0,0 +1,347 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * xattr_writer_flush.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "xattr_writer.h" + +static const char *hexmap = "0123456789ABCDEF"; + +static void *from_base32(const char *input, size_t *size_out) +{ + sqfs_u8 lo, hi, *out, *ptr; + size_t len; + + len = strlen(input); + *size_out = len / 2; + + out = malloc(*size_out); + if (out == NULL) + return NULL; + + ptr = out; + + while (*input != '\0') { + lo = strchr(hexmap, *(input++)) - hexmap; + hi = strchr(hexmap, *(input++)) - hexmap; + + *(ptr++) = lo | (hi << 4); + } + + return out; +} + +static sqfs_s32 write_key(sqfs_meta_writer_t *mw, const char *key, + bool value_is_ool) +{ + sqfs_xattr_entry_t kent; + int type, err; + size_t len; + + type = sqfs_get_xattr_prefix_id(key); + assert(type >= 0); + + key = strchr(key, '.'); + assert(key != NULL); + ++key; + len = strlen(key); + + if (value_is_ool) + type |= SQFS_XATTR_FLAG_OOL; + + memset(&kent, 0, sizeof(kent)); + kent.type = htole16(type); + kent.size = htole16(len); + + err = sqfs_meta_writer_append(mw, &kent, sizeof(kent)); + if (err) + return err; + + err = sqfs_meta_writer_append(mw, key, len); + if (err) + return err; + + return sizeof(kent) + len; +} + +static sqfs_s32 write_value(sqfs_meta_writer_t *mw, const char *value_str, + sqfs_u64 *value_ref_out) +{ + sqfs_xattr_value_t vent; + sqfs_u32 offset; + sqfs_u64 block; + size_t size; + void *value; + int err; + + value = from_base32(value_str, &size); + if (value == NULL) + return SQFS_ERROR_ALLOC; + + memset(&vent, 0, sizeof(vent)); + vent.size = htole32(size); + + sqfs_meta_writer_get_position(mw, &block, &offset); + *value_ref_out = (block << 16) | (offset & 0xFFFF); + + err = sqfs_meta_writer_append(mw, &vent, sizeof(vent)); + if (err) + goto fail; + + err = sqfs_meta_writer_append(mw, value, size); + if (err) + goto fail; + + free(value); + return sizeof(vent) + size; +fail: + free(value); + return err; +} + +static sqfs_s32 write_value_ool(sqfs_meta_writer_t *mw, sqfs_u64 location) +{ + sqfs_xattr_value_t vent; + sqfs_u64 ref; + int err; + + memset(&vent, 0, sizeof(vent)); + vent.size = htole32(sizeof(location)); + ref = htole64(location); + + err = sqfs_meta_writer_append(mw, &vent, sizeof(vent)); + if (err) + return err; + + err = sqfs_meta_writer_append(mw, &ref, sizeof(ref)); + if (err) + return err; + + return sizeof(vent) + sizeof(ref); +} + +static bool should_store_ool(const char *val_str, size_t refcount) +{ + if (refcount < 2) + return false; + + /* + Storing in line needs this many bytes: refcount * len + + Storing out-of-line needs this many: len + (refcount - 1) * 8 + + Out-of-line prefereable iff refcount > 1 and: + refcount * len > len + (refcount - 1) * 8 + => refcount * len - len > (refcount - 1) * 8 + => (refcount - 1) * len > (refcount - 1) * 8 + => len > 8 + */ + return (strlen(val_str) / 2) > sizeof(sqfs_u64); +} + +static int write_block_pairs(const sqfs_xattr_writer_t *xwr, + sqfs_meta_writer_t *mw, + const kv_block_desc_t *blk, + sqfs_u64 *ool_locations) +{ + const char *key_str, *value_str; + sqfs_s32 diff, total = 0; + size_t i, refcount; + sqfs_u64 ref; + + for (i = 0; i < blk->count; ++i) { + sqfs_u64 ent = ((sqfs_u64 *)xwr->kv_pairs.data)[blk->start + i]; + sqfs_u32 key_idx = GET_KEY(ent); + sqfs_u32 val_idx = GET_VALUE(ent); + + key_str = str_table_get_string(&xwr->keys, key_idx); + value_str = str_table_get_string(&xwr->values, val_idx); + + if (ool_locations[val_idx] == 0xFFFFFFFFFFFFFFFFUL) { + diff = write_key(mw, key_str, false); + if (diff < 0) + return diff; + total += diff; + + diff = write_value(mw, value_str, &ref); + if (diff < 0) + return diff; + total += diff; + + refcount = str_table_get_ref_count(&xwr->values, + val_idx); + + if (should_store_ool(value_str, refcount)) + ool_locations[val_idx] = ref; + } else { + diff = write_key(mw, key_str, true); + if (diff < 0) + return diff; + total += diff; + + diff = write_value_ool(mw, ool_locations[val_idx]); + if (diff < 0) + return diff; + total += diff; + } + } + + return total; +} + +static int write_kv_pairs(const sqfs_xattr_writer_t *xwr, + sqfs_meta_writer_t *mw) +{ + sqfs_u64 block, *ool_locations; + kv_block_desc_t *blk; + sqfs_u32 offset; + sqfs_s32 size; + size_t i; + + ool_locations = alloc_array(sizeof(ool_locations[0]), + str_table_count(&xwr->values)); + if (ool_locations == NULL) + return SQFS_ERROR_ALLOC; + + for (i = 0; i < str_table_count(&xwr->values); ++i) + ool_locations[i] = 0xFFFFFFFFFFFFFFFFUL; + + for (blk = xwr->kv_block_first; blk != NULL; blk = blk->next) { + sqfs_meta_writer_get_position(mw, &block, &offset); + blk->start_ref = (block << 16) | (offset & 0xFFFF); + + size = write_block_pairs(xwr, mw, blk, ool_locations); + if (size < 0) { + free(ool_locations); + return size; + } + + blk->size_bytes = size; + } + + free(ool_locations); + return sqfs_meta_writer_flush(mw); +} + +static int write_id_table(const sqfs_xattr_writer_t *xwr, + sqfs_meta_writer_t *mw, + sqfs_u64 *locations) +{ + sqfs_xattr_id_t id_ent; + kv_block_desc_t *blk; + sqfs_u32 offset; + sqfs_u64 block; + size_t i = 0; + int err; + + locations[i++] = 0; + + for (blk = xwr->kv_block_first; blk != NULL; blk = blk->next) { + memset(&id_ent, 0, sizeof(id_ent)); + id_ent.xattr = htole64(blk->start_ref); + id_ent.count = htole32(blk->count); + id_ent.size = htole32(blk->size_bytes); + + err = sqfs_meta_writer_append(mw, &id_ent, sizeof(id_ent)); + if (err) + return err; + + sqfs_meta_writer_get_position(mw, &block, &offset); + if (block != locations[i - 1]) + locations[i++] = block; + } + + return sqfs_meta_writer_flush(mw); +} + +static int write_location_table(const sqfs_xattr_writer_t *xwr, + sqfs_u64 kv_start, sqfs_file_t *file, + const sqfs_super_t *super, sqfs_u64 *locations, + size_t loc_count) +{ + sqfs_xattr_id_table_t idtbl; + int err; + + memset(&idtbl, 0, sizeof(idtbl)); + idtbl.xattr_table_start = htole64(kv_start); + idtbl.xattr_ids = htole32(xwr->num_blocks); + + err = file->write_at(file, super->xattr_id_table_start, + &idtbl, sizeof(idtbl)); + if (err) + return err; + + return file->write_at(file, super->xattr_id_table_start + sizeof(idtbl), + locations, sizeof(locations[0]) * loc_count); +} + +static int alloc_location_table(const sqfs_xattr_writer_t *xwr, + sqfs_u64 **tbl_out, size_t *szout) +{ + sqfs_u64 *locations; + size_t size, count; + + if (SZ_MUL_OV(xwr->num_blocks, sizeof(sqfs_xattr_id_t), &size)) + return SQFS_ERROR_OVERFLOW; + + count = size / SQFS_META_BLOCK_SIZE; + if (size % SQFS_META_BLOCK_SIZE) + ++count; + + locations = alloc_array(sizeof(sqfs_u64), count); + if (locations == NULL) + return SQFS_ERROR_ALLOC; + + *tbl_out = locations; + *szout = count; + return 0; +} + +int sqfs_xattr_writer_flush(const sqfs_xattr_writer_t *xwr, sqfs_file_t *file, + sqfs_super_t *super, sqfs_compressor_t *cmp) +{ + sqfs_u64 *locations = NULL, kv_start, id_start; + sqfs_meta_writer_t *mw; + size_t i, count; + int err; + + if (xwr->kv_pairs.used == 0 || xwr->num_blocks == 0) { + super->xattr_id_table_start = 0xFFFFFFFFFFFFFFFFUL; + super->flags |= SQFS_FLAG_NO_XATTRS; + return 0; + } + + mw = sqfs_meta_writer_create(file, cmp, 0); + if (mw == NULL) + return SQFS_ERROR_ALLOC; + + kv_start = file->get_size(file); + err = write_kv_pairs(xwr, mw); + if (err) + goto out; + + sqfs_meta_writer_reset(mw); + + id_start = file->get_size(file); + err = alloc_location_table(xwr, &locations, &count); + if (err) + goto out; + + err = write_id_table(xwr, mw, locations); + if (err) + goto out; + + super->xattr_id_table_start = file->get_size(file); + super->flags &= ~SQFS_FLAG_NO_XATTRS; + + for (i = 0; i < count; ++i) + locations[i] = htole64(locations[i] + id_start); + + err = write_location_table(xwr, kv_start, file, super, + locations, count); +out: + free(locations); + sqfs_drop(mw); + return err; +} diff --git a/lib/sqfs/src/xattr/xattr_writer_record.c b/lib/sqfs/src/xattr/xattr_writer_record.c new file mode 100644 index 0000000..81bbf6b --- /dev/null +++ b/lib/sqfs/src/xattr/xattr_writer_record.c @@ -0,0 +1,145 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * xattr_writer_record.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "xattr_writer.h" + +static const char *hexmap = "0123456789ABCDEF"; + +static char *to_base32(const void *input, size_t size) +{ + const sqfs_u8 *in = input; + char *out, *ptr; + size_t i; + + out = malloc(2 * size + 1); + if (out == NULL) + return NULL; + + ptr = out; + + for (i = 0; i < size; ++i) { + *(ptr++) = hexmap[ in[i] & 0x0F]; + *(ptr++) = hexmap[(in[i] >> 4) & 0x0F]; + } + + *ptr = '\0'; + return out; +} + +static int compare_u64(const void *a, const void *b) +{ + sqfs_u64 lhs = *((const sqfs_u64 *)a); + sqfs_u64 rhs = *((const sqfs_u64 *)b); + + return (lhs < rhs ? -1 : (lhs > rhs ? 1 : 0)); +} + +int sqfs_xattr_writer_begin(sqfs_xattr_writer_t *xwr, sqfs_u32 flags) +{ + if (flags != 0) + return SQFS_ERROR_UNSUPPORTED; + + xwr->kv_start = xwr->kv_pairs.used; + return 0; +} + +int sqfs_xattr_writer_add(sqfs_xattr_writer_t *xwr, const char *key, + const void *value, size_t size) +{ + size_t i, key_index, old_value_index, value_index; + sqfs_u64 kv_pair; + char *value_str; + int err; + + if (sqfs_get_xattr_prefix_id(key) < 0) + return SQFS_ERROR_UNSUPPORTED; + + err = str_table_get_index(&xwr->keys, key, &key_index); + if (err) + return err; + + value_str = to_base32(value, size); + if (value_str == NULL) + return SQFS_ERROR_ALLOC; + + err = str_table_get_index(&xwr->values, value_str, &value_index); + free(value_str); + if (err) + return err; + + str_table_add_ref(&xwr->values, value_index); + + if (sizeof(size_t) > sizeof(sqfs_u32)) { + if (key_index > 0x0FFFFFFFFUL || value_index > 0x0FFFFFFFFUL) + return SQFS_ERROR_OVERFLOW; + } + + kv_pair = MK_PAIR(key_index, value_index); + + for (i = xwr->kv_start; i < xwr->kv_pairs.used; ++i) { + sqfs_u64 ent = ((sqfs_u64 *)xwr->kv_pairs.data)[i]; + + if (ent == kv_pair) + return 0; + + if (GET_KEY(ent) == key_index) { + old_value_index = GET_VALUE(ent); + + str_table_del_ref(&xwr->values, old_value_index); + + ((sqfs_u64 *)xwr->kv_pairs.data)[i] = kv_pair; + return 0; + } + } + + return array_append(&xwr->kv_pairs, &kv_pair); +} + +int sqfs_xattr_writer_end(sqfs_xattr_writer_t *xwr, sqfs_u32 *out) +{ + kv_block_desc_t blk; + rbtree_node_t *n; + sqfs_u32 index; + int ret; + + memset(&blk, 0, sizeof(blk)); + blk.start = xwr->kv_start; + blk.count = xwr->kv_pairs.used - xwr->kv_start; + + if (blk.count == 0) { + *out = 0xFFFFFFFF; + return 0; + } + + array_sort_range(&xwr->kv_pairs, blk.start, blk.count, compare_u64); + + n = rbtree_lookup(&xwr->kv_block_tree, &blk); + + if (n != NULL) { + index = *((sqfs_u32 *)rbtree_node_value(n)); + xwr->kv_pairs.used = xwr->kv_start; + } else { + index = xwr->num_blocks; + + ret = rbtree_insert(&xwr->kv_block_tree, &blk, &index); + if (ret != 0) + return ret; + + xwr->num_blocks += 1; + n = rbtree_lookup(&xwr->kv_block_tree, &blk); + + if (xwr->kv_block_last == NULL) { + xwr->kv_block_first = rbtree_node_key(n); + xwr->kv_block_last = xwr->kv_block_first; + } else { + xwr->kv_block_last->next = rbtree_node_key(n); + xwr->kv_block_last = xwr->kv_block_last->next; + } + } + + *out = index; + return 0; +} diff --git a/lib/sqfs/super.c b/lib/sqfs/super.c deleted file mode 100644 index 470c06a..0000000 --- a/lib/sqfs/super.c +++ /dev/null @@ -1,50 +0,0 @@ -/* SPDX-License-Identifier: LGPL-3.0-or-later */ -/* - * super.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#define SQFS_BUILDING_DLL -#include "config.h" - -#include "sqfs/super.h" -#include "sqfs/error.h" - -#include - -int sqfs_super_init(sqfs_super_t *super, size_t block_size, sqfs_u32 mtime, - SQFS_COMPRESSOR compressor) -{ - unsigned int i; - - if (block_size & (block_size - 1)) - return SQFS_ERROR_SUPER_BLOCK_SIZE; - - if (block_size < SQFS_MIN_BLOCK_SIZE) - return SQFS_ERROR_SUPER_BLOCK_SIZE; - - if (block_size > SQFS_MAX_BLOCK_SIZE) - return SQFS_ERROR_SUPER_BLOCK_SIZE; - - memset(super, 0, sizeof(*super)); - super->magic = SQFS_MAGIC; - super->modification_time = mtime; - super->block_size = block_size; - super->compression_id = compressor; - super->flags = SQFS_FLAG_NO_FRAGMENTS | SQFS_FLAG_NO_XATTRS; - super->flags |= SQFS_FLAG_NO_DUPLICATES; - super->version_major = SQFS_VERSION_MAJOR; - super->version_minor = SQFS_VERSION_MINOR; - super->bytes_used = sizeof(*super); - super->id_table_start = 0xFFFFFFFFFFFFFFFFUL; - super->xattr_id_table_start = 0xFFFFFFFFFFFFFFFFUL; - super->inode_table_start = 0xFFFFFFFFFFFFFFFFUL; - super->directory_table_start = 0xFFFFFFFFFFFFFFFFUL; - super->fragment_table_start = 0xFFFFFFFFFFFFFFFFUL; - super->export_table_start = 0xFFFFFFFFFFFFFFFFUL; - - for (i = block_size; i != 0x01; i >>= 1) - super->block_log += 1; - - return 0; -} diff --git a/lib/sqfs/unix/io_file.c b/lib/sqfs/unix/io_file.c deleted file mode 100644 index e1fb9db..0000000 --- a/lib/sqfs/unix/io_file.c +++ /dev/null @@ -1,196 +0,0 @@ -/* SPDX-License-Identifier: LGPL-3.0-or-later */ -/* - * io_file.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#define SQFS_BUILDING_DLL -#include "config.h" - -#include "sqfs/io.h" -#include "sqfs/error.h" - -#include -#include -#include -#include -#include -#include - - -typedef struct { - sqfs_file_t base; - - bool readonly; - sqfs_u64 size; - int fd; -} sqfs_file_stdio_t; - - -static void stdio_destroy(sqfs_object_t *base) -{ - sqfs_file_stdio_t *file = (sqfs_file_stdio_t *)base; - - close(file->fd); - free(file); -} - -static sqfs_object_t *stdio_copy(const sqfs_object_t *base) -{ - const sqfs_file_stdio_t *file = (const sqfs_file_stdio_t *)base; - sqfs_file_stdio_t *copy; - int err; - - if (!file->readonly) { - errno = ENOTSUP; - return NULL; - } - - copy = calloc(1, sizeof(*copy)); - if (copy == NULL) - return NULL; - - memcpy(copy, file, sizeof(*file)); - - copy->fd = dup(file->fd); - if (copy->fd < 0) { - err = errno; - free(copy); - copy = NULL; - errno = err; - } - - return (sqfs_object_t *)copy; -} - -static int stdio_read_at(sqfs_file_t *base, sqfs_u64 offset, - void *buffer, size_t size) -{ - sqfs_file_stdio_t *file = (sqfs_file_stdio_t *)base; - ssize_t ret; - - while (size > 0) { - ret = pread(file->fd, buffer, size, offset); - - if (ret < 0) { - if (errno == EINTR) - continue; - return SQFS_ERROR_IO; - } - - if (ret == 0) - return SQFS_ERROR_OUT_OF_BOUNDS; - - buffer = (char *)buffer + ret; - size -= ret; - offset += ret; - } - - return 0; -} - -static int stdio_write_at(sqfs_file_t *base, sqfs_u64 offset, - const void *buffer, size_t size) -{ - sqfs_file_stdio_t *file = (sqfs_file_stdio_t *)base; - ssize_t ret; - - while (size > 0) { - ret = pwrite(file->fd, buffer, size, offset); - - if (ret < 0) { - if (errno == EINTR) - continue; - return SQFS_ERROR_IO; - } - - if (ret == 0) - return SQFS_ERROR_OUT_OF_BOUNDS; - - buffer = (const char *)buffer + ret; - size -= ret; - offset += ret; - } - - if (offset >= file->size) - file->size = offset; - - return 0; -} - -static sqfs_u64 stdio_get_size(const sqfs_file_t *base) -{ - const sqfs_file_stdio_t *file = (const sqfs_file_stdio_t *)base; - - return file->size; -} - -static int stdio_truncate(sqfs_file_t *base, sqfs_u64 size) -{ - sqfs_file_stdio_t *file = (sqfs_file_stdio_t *)base; - - if (ftruncate(file->fd, size)) - return SQFS_ERROR_IO; - - file->size = size; - return 0; -} - - -sqfs_file_t *sqfs_open_file(const char *filename, sqfs_u32 flags) -{ - sqfs_file_stdio_t *file; - int open_mode, temp; - sqfs_file_t *base; - struct stat sb; - - if (flags & ~SQFS_FILE_OPEN_ALL_FLAGS) { - errno = EINVAL; - return NULL; - } - - file = calloc(1, sizeof(*file)); - base = (sqfs_file_t *)file; - if (file == NULL) - return NULL; - - sqfs_object_init(file, stdio_destroy, stdio_copy); - - if (flags & SQFS_FILE_OPEN_READ_ONLY) { - file->readonly = true; - open_mode = O_RDONLY; - } else { - file->readonly = false; - open_mode = O_CREAT | O_RDWR; - - if (flags & SQFS_FILE_OPEN_OVERWRITE) { - open_mode |= O_TRUNC; - } else { - open_mode |= O_EXCL; - } - } - - file->fd = open(filename, open_mode, 0644); - if (file->fd < 0) { - temp = errno; - free(file); - errno = temp; - return NULL; - } - - if (fstat(file->fd, &sb)) { - temp = errno; - close(file->fd); - free(file); - errno = temp; - return NULL; - } - - file->size = sb.st_size; - - base->read_at = stdio_read_at; - base->write_at = stdio_write_at; - base->get_size = stdio_get_size; - base->truncate = stdio_truncate; - return base; -} diff --git a/lib/sqfs/win32/io_file.c b/lib/sqfs/win32/io_file.c deleted file mode 100644 index 548a246..0000000 --- a/lib/sqfs/win32/io_file.c +++ /dev/null @@ -1,237 +0,0 @@ -/* SPDX-License-Identifier: LGPL-3.0-or-later */ -/* - * io_file.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#define SQFS_BUILDING_DLL -#include "config.h" - -#include "sqfs/io.h" -#include "sqfs/error.h" - -#include - -#define WIN32_LEAN_AND_MEAN -#include - - -typedef struct { - sqfs_file_t base; - - bool readonly; - sqfs_u64 size; - HANDLE fd; -} sqfs_file_stdio_t; - - -static void stdio_destroy(sqfs_object_t *base) -{ - sqfs_file_stdio_t *file = (sqfs_file_stdio_t *)base; - - CloseHandle(file->fd); - free(file); -} - -static sqfs_object_t *stdio_copy(const sqfs_object_t *base) -{ - const sqfs_file_stdio_t *file = (const sqfs_file_stdio_t *)base; - sqfs_file_stdio_t *copy; - BOOL ret; - - if (!file->readonly) { - SetLastError(ERROR_NOT_SUPPORTED); - return NULL; - } - - copy = calloc(1, sizeof(*copy)); - if (copy == NULL) - return NULL; - - memcpy(copy, file, sizeof(*file)); - - ret = DuplicateHandle(GetCurrentProcess(), file->fd, - GetCurrentProcess(), ©->fd, - 0, FALSE, DUPLICATE_SAME_ACCESS); - - if (!ret) { - free(copy); - return NULL; - } - - return (sqfs_object_t *)copy; -} - -static int stdio_read_at(sqfs_file_t *base, sqfs_u64 offset, - void *buffer, size_t size) -{ - sqfs_file_stdio_t *file = (sqfs_file_stdio_t *)base; - DWORD actually_read; - LARGE_INTEGER pos; - - if (offset >= file->size) - return SQFS_ERROR_OUT_OF_BOUNDS; - - if (size == 0) - return 0; - - if ((offset + size - 1) >= file->size) - return SQFS_ERROR_OUT_OF_BOUNDS; - - pos.QuadPart = offset; - - if (!SetFilePointerEx(file->fd, pos, NULL, FILE_BEGIN)) - return SQFS_ERROR_IO; - - while (size > 0) { - if (!ReadFile(file->fd, buffer, size, &actually_read, NULL)) - return SQFS_ERROR_IO; - - size -= actually_read; - buffer = (char *)buffer + actually_read; - } - - return 0; -} - -static int stdio_write_at(sqfs_file_t *base, sqfs_u64 offset, - const void *buffer, size_t size) -{ - sqfs_file_stdio_t *file = (sqfs_file_stdio_t *)base; - DWORD actually_read; - LARGE_INTEGER pos; - - if (size == 0) - return 0; - - pos.QuadPart = offset; - - if (!SetFilePointerEx(file->fd, pos, NULL, FILE_BEGIN)) - return SQFS_ERROR_IO; - - while (size > 0) { - if (!WriteFile(file->fd, buffer, size, &actually_read, NULL)) - return SQFS_ERROR_IO; - - size -= actually_read; - buffer = (char *)buffer + actually_read; - offset += actually_read; - - if (offset > file->size) - file->size = offset; - } - - return 0; -} - -static sqfs_u64 stdio_get_size(const sqfs_file_t *base) -{ - const sqfs_file_stdio_t *file = (const sqfs_file_stdio_t *)base; - - return file->size; -} - -static int stdio_truncate(sqfs_file_t *base, sqfs_u64 size) -{ - sqfs_file_stdio_t *file = (sqfs_file_stdio_t *)base; - LARGE_INTEGER pos; - - pos.QuadPart = size; - - if (!SetFilePointerEx(file->fd, pos, NULL, FILE_BEGIN)) - return SQFS_ERROR_IO; - - if (!SetEndOfFile(file->fd)) - return SQFS_ERROR_IO; - - file->size = size; - return 0; -} - - -sqfs_file_t *sqfs_open_file(const char *filename, sqfs_u32 flags) -{ - int access_flags, creation_mode, share_mode; - sqfs_file_stdio_t *file; - LARGE_INTEGER size; - sqfs_file_t *base; - WCHAR *wpath = NULL; - DWORD length; - - if (flags & ~SQFS_FILE_OPEN_ALL_FLAGS) { - SetLastError(ERROR_INVALID_PARAMETER); - return NULL; - } - - if (!(flags & SQFS_FILE_OPEN_NO_CHARSET_XFRM)) { - length = MultiByteToWideChar(CP_UTF8, 0, filename, -1, NULL, 0); - if (length <= 0) - return NULL; - - wpath = calloc(sizeof(wpath[0]), length + 1); - if (wpath == NULL) { - SetLastError(ERROR_NOT_ENOUGH_MEMORY); - return NULL; - } - - MultiByteToWideChar(CP_UTF8, 0, filename, -1, - wpath, length + 1); - wpath[length] = '\0'; - } - - file = calloc(1, sizeof(*file)); - base = (sqfs_file_t *)file; - if (file == NULL) { - free(wpath); - SetLastError(ERROR_NOT_ENOUGH_MEMORY); - return NULL; - } - - sqfs_object_init(file, stdio_destroy, stdio_copy); - - if (flags & SQFS_FILE_OPEN_READ_ONLY) { - file->readonly = true; - access_flags = GENERIC_READ; - creation_mode = OPEN_EXISTING; - share_mode = FILE_SHARE_READ; - } else { - file->readonly = false; - access_flags = GENERIC_READ | GENERIC_WRITE; - share_mode = 0; - - if (flags & SQFS_FILE_OPEN_OVERWRITE) { - creation_mode = CREATE_ALWAYS; - } else { - creation_mode = CREATE_NEW; - } - } - - if (flags & SQFS_FILE_OPEN_NO_CHARSET_XFRM) { - file->fd = CreateFileA(filename, access_flags, share_mode, NULL, - creation_mode, FILE_ATTRIBUTE_NORMAL, - NULL); - } else { - file->fd = CreateFileW(wpath, access_flags, share_mode, NULL, - creation_mode, FILE_ATTRIBUTE_NORMAL, - NULL); - } - - free(wpath); - - if (file->fd == INVALID_HANDLE_VALUE) { - free(file); - return NULL; - } - - if (!GetFileSizeEx(file->fd, &size)) { - free(file); - return NULL; - } - - file->size = size.QuadPart; - base->read_at = stdio_read_at; - base->write_at = stdio_write_at; - base->get_size = stdio_get_size; - base->truncate = stdio_truncate; - return base; -} diff --git a/lib/sqfs/write_inode.c b/lib/sqfs/write_inode.c deleted file mode 100644 index 118b713..0000000 --- a/lib/sqfs/write_inode.c +++ /dev/null @@ -1,220 +0,0 @@ -/* SPDX-License-Identifier: LGPL-3.0-or-later */ -/* - * write_inode.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#define SQFS_BUILDING_DLL -#include "config.h" - -#include "sqfs/meta_writer.h" -#include "sqfs/error.h" -#include "sqfs/inode.h" -#include "sqfs/dir.h" -#include "compat.h" - -#include -#include - -#if defined(_WIN32) || defined(__WINDOWS__) -# include -# ifdef _MSC_VER -# define alloca _alloca -# endif -#elif defined(HAVE_ALLOCA_H) -# include -#endif - -static int write_block_sizes(sqfs_meta_writer_t *ir, - const sqfs_inode_generic_t *n) -{ - sqfs_u32 *sizes; - size_t i; - - if (n->payload_bytes_used < sizeof(sizes[0])) - return 0; - - if ((n->payload_bytes_used % sizeof(sizes[0])) != 0) - return SQFS_ERROR_CORRUPTED; - - sizes = alloca(n->payload_bytes_used); - - for (i = 0; i < (n->payload_bytes_used / sizeof(sizes[0])); ++i) - sizes[i] = htole32(n->extra[i]); - - return sqfs_meta_writer_append(ir, sizes, n->payload_bytes_used); -} - -static int write_dir_index(sqfs_meta_writer_t *ir, const sqfs_u8 *data, - size_t count) -{ - sqfs_dir_index_t ent; - size_t len; - int err; - - while (count > sizeof(ent)) { - memcpy(&ent, data, sizeof(ent)); - data += sizeof(ent); - count -= sizeof(ent); - len = ent.size + 1; - - if (len > count) - return SQFS_ERROR_CORRUPTED; - - ent.start_block = htole32(ent.start_block); - ent.index = htole32(ent.index); - ent.size = htole32(ent.size); - - err = sqfs_meta_writer_append(ir, &ent, sizeof(ent)); - if (err) - return err; - - err = sqfs_meta_writer_append(ir, data, len); - if (err) - return err; - - data += len; - count -= len; - } - - return 0; -} - -int sqfs_meta_writer_write_inode(sqfs_meta_writer_t *ir, - const sqfs_inode_generic_t *n) -{ - sqfs_inode_t base; - int ret; - - base.type = htole16(n->base.type); - base.mode = htole16(n->base.mode & ~SQFS_INODE_MODE_MASK); - base.uid_idx = htole16(n->base.uid_idx); - base.gid_idx = htole16(n->base.gid_idx); - base.mod_time = htole32(n->base.mod_time); - base.inode_number = htole32(n->base.inode_number); - - ret = sqfs_meta_writer_append(ir, &base, sizeof(base)); - if (ret) - return ret; - - switch (n->base.type) { - case SQFS_INODE_DIR: { - sqfs_inode_dir_t dir = { - .start_block = htole32(n->data.dir.start_block), - .nlink = htole32(n->data.dir.nlink), - .size = htole16(n->data.dir.size), - .offset = htole16(n->data.dir.offset), - .parent_inode = htole32(n->data.dir.parent_inode), - }; - return sqfs_meta_writer_append(ir, &dir, sizeof(dir)); - } - case SQFS_INODE_FILE: { - sqfs_inode_file_t file = { - .blocks_start = htole32(n->data.file.blocks_start), - .fragment_index = htole32(n->data.file.fragment_index), - .fragment_offset = - htole32(n->data.file.fragment_offset), - .file_size = htole32(n->data.file.file_size), - }; - ret = sqfs_meta_writer_append(ir, &file, sizeof(file)); - if (ret) - return ret; - return write_block_sizes(ir, n); - } - case SQFS_INODE_SLINK: { - sqfs_inode_slink_t slink = { - .nlink = htole32(n->data.slink.nlink), - .target_size = htole32(n->data.slink.target_size), - }; - ret = sqfs_meta_writer_append(ir, &slink, sizeof(slink)); - if (ret) - return ret; - return sqfs_meta_writer_append(ir, n->extra, - n->data.slink.target_size); - } - case SQFS_INODE_BDEV: - case SQFS_INODE_CDEV: { - sqfs_inode_dev_t dev = { - .nlink = htole32(n->data.dev.nlink), - .devno = htole32(n->data.dev.devno), - }; - return sqfs_meta_writer_append(ir, &dev, sizeof(dev)); - } - case SQFS_INODE_FIFO: - case SQFS_INODE_SOCKET: { - sqfs_inode_ipc_t ipc = { - .nlink = htole32(n->data.ipc.nlink), - }; - return sqfs_meta_writer_append(ir, &ipc, sizeof(ipc)); - } - case SQFS_INODE_EXT_DIR: { - sqfs_inode_dir_ext_t dir = { - .nlink = htole32(n->data.dir_ext.nlink), - .size = htole32(n->data.dir_ext.size), - .start_block = htole32(n->data.dir_ext.start_block), - .parent_inode = htole32(n->data.dir_ext.parent_inode), - .inodex_count = htole16(n->data.dir_ext.inodex_count), - .offset = htole16(n->data.dir_ext.offset), - .xattr_idx = htole32(n->data.dir_ext.xattr_idx), - }; - ret = sqfs_meta_writer_append(ir, &dir, sizeof(dir)); - if (ret) - return ret; - return write_dir_index(ir, (const sqfs_u8 *)n->extra, - n->payload_bytes_used); - } - case SQFS_INODE_EXT_FILE: { - sqfs_inode_file_ext_t file = { - .blocks_start = htole64(n->data.file_ext.blocks_start), - .file_size = htole64(n->data.file_ext.file_size), - .sparse = htole64(n->data.file_ext.sparse), - .nlink = htole32(n->data.file_ext.nlink), - .fragment_idx = htole32(n->data.file_ext.fragment_idx), - .fragment_offset = - htole32(n->data.file_ext.fragment_offset), - .xattr_idx = htole32(n->data.file_ext.xattr_idx), - }; - ret = sqfs_meta_writer_append(ir, &file, sizeof(file)); - if (ret) - return ret; - return write_block_sizes(ir, n); - } - case SQFS_INODE_EXT_SLINK: { - sqfs_inode_slink_t slink = { - .nlink = htole32(n->data.slink_ext.nlink), - .target_size = htole32(n->data.slink_ext.target_size), - }; - sqfs_u32 xattr = htole32(n->data.slink_ext.xattr_idx); - - ret = sqfs_meta_writer_append(ir, &slink, sizeof(slink)); - if (ret) - return ret; - ret = sqfs_meta_writer_append(ir, n->extra, - n->data.slink_ext.target_size); - if (ret) - return ret; - return sqfs_meta_writer_append(ir, &xattr, sizeof(xattr)); - } - case SQFS_INODE_EXT_BDEV: - case SQFS_INODE_EXT_CDEV: { - sqfs_inode_dev_ext_t dev = { - .nlink = htole32(n->data.dev_ext.nlink), - .devno = htole32(n->data.dev_ext.devno), - .xattr_idx = htole32(n->data.dev_ext.xattr_idx), - }; - return sqfs_meta_writer_append(ir, &dev, sizeof(dev)); - } - case SQFS_INODE_EXT_FIFO: - case SQFS_INODE_EXT_SOCKET: { - sqfs_inode_ipc_ext_t ipc = { - .nlink = htole32(n->data.ipc_ext.nlink), - .xattr_idx = htole32(n->data.ipc_ext.xattr_idx), - }; - return sqfs_meta_writer_append(ir, &ipc, sizeof(ipc)); - } - default: - break; - } - - return SQFS_ERROR_UNSUPPORTED; -} diff --git a/lib/sqfs/write_super.c b/lib/sqfs/write_super.c deleted file mode 100644 index 35127da..0000000 --- a/lib/sqfs/write_super.c +++ /dev/null @@ -1,39 +0,0 @@ -/* SPDX-License-Identifier: LGPL-3.0-or-later */ -/* - * write_super.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#define SQFS_BUILDING_DLL -#include "config.h" - -#include "sqfs/super.h" -#include "sqfs/io.h" -#include "compat.h" - -int sqfs_super_write(const sqfs_super_t *super, sqfs_file_t *file) -{ - sqfs_super_t copy; - - copy.magic = htole32(super->magic); - copy.inode_count = htole32(super->inode_count); - copy.modification_time = htole32(super->modification_time); - copy.block_size = htole32(super->block_size); - copy.fragment_entry_count = htole32(super->fragment_entry_count); - copy.compression_id = htole16(super->compression_id); - copy.block_log = htole16(super->block_log); - copy.flags = htole16(super->flags); - copy.id_count = htole16(super->id_count); - copy.version_major = htole16(super->version_major); - copy.version_minor = htole16(super->version_minor); - copy.root_inode_ref = htole64(super->root_inode_ref); - copy.bytes_used = htole64(super->bytes_used); - copy.id_table_start = htole64(super->id_table_start); - copy.xattr_id_table_start = htole64(super->xattr_id_table_start); - copy.inode_table_start = htole64(super->inode_table_start); - copy.directory_table_start = htole64(super->directory_table_start); - copy.fragment_table_start = htole64(super->fragment_table_start); - copy.export_table_start = htole64(super->export_table_start); - - return file->write_at(file, 0, ©, sizeof(copy)); -} diff --git a/lib/sqfs/write_table.c b/lib/sqfs/write_table.c deleted file mode 100644 index 6f28a75..0000000 --- a/lib/sqfs/write_table.c +++ /dev/null @@ -1,81 +0,0 @@ -/* SPDX-License-Identifier: LGPL-3.0-or-later */ -/* - * write_table.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#define SQFS_BUILDING_DLL -#include "config.h" - -#include "sqfs/meta_writer.h" -#include "sqfs/error.h" -#include "sqfs/super.h" -#include "sqfs/table.h" -#include "sqfs/block.h" -#include "sqfs/io.h" -#include "util/util.h" - -#include - -int sqfs_write_table(sqfs_file_t *file, sqfs_compressor_t *cmp, - const void *data, size_t table_size, sqfs_u64 *start) -{ - size_t block_count, list_size, diff, blkidx = 0; - sqfs_u64 off, *locations; - sqfs_meta_writer_t *m; - int ret; - - block_count = table_size / SQFS_META_BLOCK_SIZE; - if ((table_size % SQFS_META_BLOCK_SIZE) != 0) - ++block_count; - - locations = alloc_array(sizeof(sqfs_u64), block_count); - - if (locations == NULL) - return SQFS_ERROR_ALLOC; - - /* Write actual data */ - m = sqfs_meta_writer_create(file, cmp, 0); - if (m == NULL) { - ret = SQFS_ERROR_ALLOC; - goto out_idx; - } - - while (table_size > 0) { - locations[blkidx++] = htole64(file->get_size(file)); - - diff = SQFS_META_BLOCK_SIZE; - if (diff > table_size) - diff = table_size; - - ret = sqfs_meta_writer_append(m, data, diff); - if (ret) - goto out; - - data = (const char *)data + diff; - table_size -= diff; - } - - ret = sqfs_meta_writer_flush(m); - if (ret) - goto out; - - /* write location list */ - *start = file->get_size(file); - - list_size = sizeof(sqfs_u64) * block_count; - - off = file->get_size(file); - - ret = file->write_at(file, off, locations, list_size); - if (ret) - goto out; - - /* cleanup */ - ret = 0; -out: - sqfs_drop(m); -out_idx: - free(locations); - return ret; -} diff --git a/lib/sqfs/xattr/xattr.c b/lib/sqfs/xattr/xattr.c deleted file mode 100644 index 29ecebf..0000000 --- a/lib/sqfs/xattr/xattr.c +++ /dev/null @@ -1,49 +0,0 @@ -/* SPDX-License-Identifier: LGPL-3.0-or-later */ -/* - * write_xattr.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#define SQFS_BUILDING_DLL -#include "config.h" -#include "sqfs/xattr.h" -#include "sqfs/error.h" - -#include - -static const struct { - const char *prefix; - SQFS_XATTR_TYPE type; -} xattr_types[] = { - { "user.", SQFS_XATTR_USER }, - { "trusted.", SQFS_XATTR_TRUSTED }, - { "security.", SQFS_XATTR_SECURITY }, -}; - -int sqfs_get_xattr_prefix_id(const char *key) -{ - size_t i, len; - - for (i = 0; i < sizeof(xattr_types) / sizeof(xattr_types[0]); ++i) { - len = strlen(xattr_types[i].prefix); - - if (strncmp(key, xattr_types[i].prefix, len) == 0 && - strlen(key) > len) { - return xattr_types[i].type; - } - } - - return SQFS_ERROR_UNSUPPORTED; -} - -const char *sqfs_get_xattr_prefix(SQFS_XATTR_TYPE id) -{ - size_t i; - - for (i = 0; i < sizeof(xattr_types) / sizeof(xattr_types[0]); ++i) { - if (xattr_types[i].type == id) - return xattr_types[i].prefix; - } - - return NULL; -} diff --git a/lib/sqfs/xattr/xattr_reader.c b/lib/sqfs/xattr/xattr_reader.c deleted file mode 100644 index 9e3ea76..0000000 --- a/lib/sqfs/xattr/xattr_reader.c +++ /dev/null @@ -1,336 +0,0 @@ -/* SPDX-License-Identifier: LGPL-3.0-or-later */ -/* - * xattr_reader.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#define SQFS_BUILDING_DLL -#include "config.h" - -#include "sqfs/xattr_reader.h" -#include "sqfs/meta_reader.h" -#include "sqfs/super.h" -#include "sqfs/xattr.h" -#include "sqfs/error.h" -#include "sqfs/block.h" -#include "sqfs/io.h" -#include "util/util.h" - -#include -#include -#include - -struct sqfs_xattr_reader_t { - sqfs_object_t base; - - sqfs_u64 xattr_start; - sqfs_u64 xattr_end; - - size_t num_id_blocks; - size_t num_ids; - - sqfs_u64 *id_block_starts; - - sqfs_meta_reader_t *idrd; - sqfs_meta_reader_t *kvrd; -}; - -static sqfs_object_t *xattr_reader_copy(const sqfs_object_t *obj) -{ - const sqfs_xattr_reader_t *xr = (const sqfs_xattr_reader_t *)obj; - sqfs_xattr_reader_t *copy = malloc(sizeof(*copy)); - - if (copy == NULL) - return NULL; - - memcpy(copy, xr, sizeof(*xr)); - - if (xr->kvrd != NULL) { - copy->kvrd = sqfs_copy(xr->kvrd); - if (copy->kvrd == NULL) - goto fail; - } - - if (xr->idrd != NULL) { - copy->idrd = sqfs_copy(xr->idrd); - if (copy->idrd == NULL) - goto fail; - } - - if (xr->id_block_starts != NULL) { - copy->id_block_starts = alloc_array(sizeof(sqfs_u64), - xr->num_id_blocks); - if (copy->id_block_starts == NULL) - goto fail; - - memcpy(copy->id_block_starts, xr->id_block_starts, - sizeof(sqfs_u64) * xr->num_id_blocks); - } - - return (sqfs_object_t *)copy; -fail: - sqfs_drop(copy->idrd); - sqfs_drop(copy->kvrd); - free(copy); - return NULL; -} - -static void xattr_reader_destroy(sqfs_object_t *obj) -{ - sqfs_xattr_reader_t *xr = (sqfs_xattr_reader_t *)obj; - - sqfs_drop(xr->kvrd); - sqfs_drop(xr->idrd); - free(xr->id_block_starts); - free(xr); -} - -int sqfs_xattr_reader_load(sqfs_xattr_reader_t *xr, const sqfs_super_t *super, - sqfs_file_t *file, sqfs_compressor_t *cmp) -{ - sqfs_xattr_id_table_t idtbl; - size_t i; - int err; - - /* sanity check */ - if (super->flags & SQFS_FLAG_NO_XATTRS) - return 0; - - if (super->xattr_id_table_start == 0xFFFFFFFFFFFFFFFF) - return 0; - - if (super->xattr_id_table_start >= super->bytes_used) - return SQFS_ERROR_OUT_OF_BOUNDS; - - /* cleanup pre-existing data */ - xr->idrd = sqfs_drop(xr->idrd); - xr->kvrd = sqfs_drop(xr->kvrd); - - free(xr->id_block_starts); - xr->id_block_starts = NULL; - - /* read the locations table */ - err = file->read_at(file, super->xattr_id_table_start, - &idtbl, sizeof(idtbl)); - if (err) - return err; - - xr->xattr_start = le64toh(idtbl.xattr_table_start); - xr->num_ids = le32toh(idtbl.xattr_ids); - xr->num_id_blocks = - (xr->num_ids * sizeof(sqfs_xattr_id_t)) / SQFS_META_BLOCK_SIZE; - - if ((xr->num_ids * sizeof(sqfs_xattr_id_t)) % SQFS_META_BLOCK_SIZE) - xr->num_id_blocks += 1; - - xr->id_block_starts = alloc_array(sizeof(sqfs_u64), xr->num_id_blocks); - if (xr->id_block_starts == NULL) { - if (errno == EOVERFLOW) - return SQFS_ERROR_OVERFLOW; - return SQFS_ERROR_ALLOC; - } - - err = file->read_at(file, super->xattr_id_table_start + sizeof(idtbl), - xr->id_block_starts, - sizeof(sqfs_u64) * xr->num_id_blocks); - if (err) - goto fail_blocks; - - for (i = 0; i < xr->num_id_blocks; ++i) { - xr->id_block_starts[i] = le64toh(xr->id_block_starts[i]); - - if (xr->id_block_starts[i] > super->bytes_used) { - err = SQFS_ERROR_OUT_OF_BOUNDS; - goto fail_blocks; - } - } - - /* create the meta data readers */ - xr->idrd = sqfs_meta_reader_create(file, cmp, super->id_table_start, - super->bytes_used); - if (xr->idrd == NULL) - goto fail_blocks; - - xr->kvrd = sqfs_meta_reader_create(file, cmp, super->id_table_start, - super->bytes_used); - if (xr->kvrd == NULL) - goto fail_idrd; - - xr->xattr_end = super->bytes_used; - return 0; -fail_idrd: - xr->idrd = sqfs_drop(xr->idrd); -fail_blocks: - free(xr->id_block_starts); - xr->id_block_starts = NULL; - return err; -} - -int sqfs_xattr_reader_read_key(sqfs_xattr_reader_t *xr, - sqfs_xattr_entry_t **key_out) -{ - sqfs_xattr_entry_t key, *out; - const char *prefix; - size_t plen, total; - int ret; - - ret = sqfs_meta_reader_read(xr->kvrd, &key, sizeof(key)); - if (ret) - return ret; - - key.type = le16toh(key.type); - key.size = le16toh(key.size); - - prefix = sqfs_get_xattr_prefix(key.type & SQFS_XATTR_PREFIX_MASK); - if (prefix == NULL) - return SQFS_ERROR_UNSUPPORTED; - - plen = strlen(prefix); - - if (SZ_ADD_OV(plen, key.size, &total) || SZ_ADD_OV(total, 1, &total) || - SZ_ADD_OV(sizeof(*out), total, &total)) { - return SQFS_ERROR_OVERFLOW; - } - - out = calloc(1, total); - if (out == NULL) - return SQFS_ERROR_ALLOC; - - *out = key; - memcpy(out->key, prefix, plen); - - ret = sqfs_meta_reader_read(xr->kvrd, out->key + plen, key.size); - if (ret) { - free(out); - return ret; - } - - *key_out = out; - return 0; -} - -int sqfs_xattr_reader_read_value(sqfs_xattr_reader_t *xr, - const sqfs_xattr_entry_t *key, - sqfs_xattr_value_t **val_out) -{ - size_t offset, new_offset, size; - sqfs_xattr_value_t value, *out; - sqfs_u64 ref, start, new_start; - int ret; - - ret = sqfs_meta_reader_read(xr->kvrd, &value, sizeof(value)); - if (ret) - return ret; - - if (key->type & SQFS_XATTR_FLAG_OOL) { - ret = sqfs_meta_reader_read(xr->kvrd, &ref, sizeof(ref)); - if (ret) - return ret; - - sqfs_meta_reader_get_position(xr->kvrd, &start, &offset); - - new_start = xr->xattr_start + (ref >> 16); - if (new_start >= xr->xattr_end) - return SQFS_ERROR_OUT_OF_BOUNDS; - - new_offset = ref & 0xFFFF; - if (new_offset >= SQFS_META_BLOCK_SIZE) - return SQFS_ERROR_OUT_OF_BOUNDS; - - ret = sqfs_meta_reader_seek(xr->kvrd, new_start, new_offset); - if (ret) - return ret; - - ret = sqfs_meta_reader_read(xr->kvrd, &value, sizeof(value)); - if (ret) - return ret; - } - - value.size = le32toh(value.size); - - if (SZ_ADD_OV(sizeof(*out), value.size, &size) || - SZ_ADD_OV(size, 1, &size)) { - return SQFS_ERROR_OVERFLOW; - } - - out = calloc(1, size); - if (out == NULL) - return SQFS_ERROR_ALLOC; - - *out = value; - - ret = sqfs_meta_reader_read(xr->kvrd, out->value, value.size); - if (ret) - goto fail; - - if (key->type & SQFS_XATTR_FLAG_OOL) { - ret = sqfs_meta_reader_seek(xr->kvrd, start, offset); - if (ret) - goto fail; - } - - *val_out = out; - return 0; -fail: - free(out); - return ret; -} - -int sqfs_xattr_reader_seek_kv(sqfs_xattr_reader_t *xr, - const sqfs_xattr_id_t *desc) -{ - sqfs_u32 offset = desc->xattr & 0xFFFF; - sqfs_u64 block = xr->xattr_start + (desc->xattr >> 16); - - return sqfs_meta_reader_seek(xr->kvrd, block, offset); -} - -int sqfs_xattr_reader_get_desc(sqfs_xattr_reader_t *xr, sqfs_u32 idx, - sqfs_xattr_id_t *desc) -{ - size_t block, offset; - int ret; - - memset(desc, 0, sizeof(*desc)); - - if (idx == 0xFFFFFFFF) - return 0; - - if (xr->kvrd == NULL || xr->idrd == NULL) - return idx == 0 ? 0 : SQFS_ERROR_OUT_OF_BOUNDS; - - if (idx >= xr->num_ids) - return SQFS_ERROR_OUT_OF_BOUNDS; - - offset = (idx * sizeof(*desc)) % SQFS_META_BLOCK_SIZE; - block = (idx * sizeof(*desc)) / SQFS_META_BLOCK_SIZE; - - ret = sqfs_meta_reader_seek(xr->idrd, xr->id_block_starts[block], - offset); - if (ret) - return ret; - - ret = sqfs_meta_reader_read(xr->idrd, desc, sizeof(*desc)); - if (ret) - return ret; - - desc->xattr = le64toh(desc->xattr); - desc->count = le32toh(desc->count); - desc->size = le32toh(desc->size); - return 0; -} - -sqfs_xattr_reader_t *sqfs_xattr_reader_create(sqfs_u32 flags) -{ - sqfs_xattr_reader_t *xr; - - if (flags != 0) - return NULL; - - xr = calloc(1, sizeof(*xr)); - if (xr == NULL) - return NULL; - - sqfs_object_init(xr, xattr_reader_destroy, xattr_reader_copy); - return xr; -} diff --git a/lib/sqfs/xattr/xattr_writer.c b/lib/sqfs/xattr/xattr_writer.c deleted file mode 100644 index 39e1b05..0000000 --- a/lib/sqfs/xattr/xattr_writer.c +++ /dev/null @@ -1,127 +0,0 @@ -/* SPDX-License-Identifier: LGPL-3.0-or-later */ -/* - * xattr_writer.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "xattr_writer.h" - -static sqfs_object_t *xattr_writer_copy(const sqfs_object_t *obj) -{ - const sqfs_xattr_writer_t *xwr = (const sqfs_xattr_writer_t *)obj; - sqfs_xattr_writer_t *copy; - kv_block_desc_t *it; - - copy = calloc(1, sizeof(*copy)); - if (copy == NULL) - return NULL; - - memcpy(copy, xwr, sizeof(*xwr)); - - if (str_table_copy(©->keys, &xwr->keys)) - goto fail_keys; - - if (str_table_copy(©->values, &xwr->values)) - goto fail_values; - - if (array_init_copy(©->kv_pairs, &xwr->kv_pairs)) - goto fail_pairs; - - if (rbtree_copy(&xwr->kv_block_tree, ©->kv_block_tree) != 0) - goto fail_tree; - - for (it = xwr->kv_block_first; it != NULL; it = it->next) { - rbtree_node_t *n = rbtree_lookup(©->kv_block_tree, it); - - if (copy->kv_block_last == NULL) { - copy->kv_block_first = rbtree_node_key(n); - copy->kv_block_last = copy->kv_block_first; - } else { - copy->kv_block_last->next = rbtree_node_key(n); - copy->kv_block_last = copy->kv_block_last->next; - } - - copy->kv_block_last->next = NULL; - } - - return (sqfs_object_t *)copy; -fail_tree: - array_cleanup(©->kv_pairs); -fail_pairs: - str_table_cleanup(©->values); -fail_values: - str_table_cleanup(©->keys); -fail_keys: - free(copy); - return NULL; -} - -static void xattr_writer_destroy(sqfs_object_t *obj) -{ - sqfs_xattr_writer_t *xwr = (sqfs_xattr_writer_t *)obj; - - rbtree_cleanup(&xwr->kv_block_tree); - array_cleanup(&xwr->kv_pairs); - str_table_cleanup(&xwr->values); - str_table_cleanup(&xwr->keys); - free(xwr); -} - -static int block_compare(const void *context, - const void *lhs, const void *rhs) -{ - const sqfs_xattr_writer_t *xwr = context; - const kv_block_desc_t *l = lhs, *r = rhs; - - if (l->count != r->count) - return l->count < r->count ? -1 : 1; - - if (l->start == r->start) - return 0; - - return memcmp((sqfs_u64 *)xwr->kv_pairs.data + l->start, - (sqfs_u64 *)xwr->kv_pairs.data + r->start, - l->count * xwr->kv_pairs.size); -} - -sqfs_xattr_writer_t *sqfs_xattr_writer_create(sqfs_u32 flags) -{ - sqfs_xattr_writer_t *xwr; - - if (flags != 0) - return NULL; - - xwr = calloc(1, sizeof(*xwr)); - if (xwr == NULL) - return NULL; - - sqfs_object_init(xwr, xattr_writer_destroy, xattr_writer_copy); - - if (str_table_init(&xwr->keys)) - goto fail_keys; - - if (str_table_init(&xwr->values)) - goto fail_values; - - if (array_init(&xwr->kv_pairs, sizeof(sqfs_u64), - XATTR_INITIAL_PAIR_CAP)) { - goto fail_pairs; - } - - if (rbtree_init(&xwr->kv_block_tree, sizeof(kv_block_desc_t), - sizeof(sqfs_u32), block_compare)) { - goto fail_tree; - } - - xwr->kv_block_tree.key_context = xwr; - return xwr; -fail_tree: - array_cleanup(&xwr->kv_pairs); -fail_pairs: - str_table_cleanup(&xwr->values); -fail_values: - str_table_cleanup(&xwr->keys); -fail_keys: - free(xwr); - return NULL; -} diff --git a/lib/sqfs/xattr/xattr_writer.h b/lib/sqfs/xattr/xattr_writer.h deleted file mode 100644 index 792cfae..0000000 --- a/lib/sqfs/xattr/xattr_writer.h +++ /dev/null @@ -1,63 +0,0 @@ -/* SPDX-License-Identifier: LGPL-3.0-or-later */ -/* - * xattr_writer.h - * - * Copyright (C) 2019 David Oberhollenzer - */ -#ifndef XATTR_WRITER_H -#define XATTR_WRITER_H - -#define SQFS_BUILDING_DLL -#include "config.h" - -#include "sqfs/xattr_writer.h" -#include "sqfs/meta_writer.h" -#include "sqfs/super.h" -#include "sqfs/xattr.h" -#include "sqfs/error.h" -#include "sqfs/block.h" -#include "sqfs/io.h" - -#include "util/str_table.h" -#include "util/rbtree.h" -#include "util/array.h" -#include "util/util.h" - -#include -#include -#include - - -#define XATTR_INITIAL_PAIR_CAP 128 - -#define MK_PAIR(key, value) (((sqfs_u64)(key) << 32UL) | (sqfs_u64)(value)) -#define GET_KEY(pair) ((pair >> 32UL) & 0x0FFFFFFFFUL) -#define GET_VALUE(pair) (pair & 0x0FFFFFFFFUL) - - -typedef struct kv_block_desc_t { - struct kv_block_desc_t *next; - size_t start; - size_t count; - - sqfs_u64 start_ref; - size_t size_bytes; -} kv_block_desc_t; - -struct sqfs_xattr_writer_t { - sqfs_object_t base; - - str_table_t keys; - str_table_t values; - - array_t kv_pairs; - - size_t kv_start; - - rbtree_t kv_block_tree; - kv_block_desc_t *kv_block_first; - kv_block_desc_t *kv_block_last; - size_t num_blocks; -}; - -#endif /* XATTR_WRITER_H */ diff --git a/lib/sqfs/xattr/xattr_writer_flush.c b/lib/sqfs/xattr/xattr_writer_flush.c deleted file mode 100644 index a06463f..0000000 --- a/lib/sqfs/xattr/xattr_writer_flush.c +++ /dev/null @@ -1,347 +0,0 @@ -/* SPDX-License-Identifier: LGPL-3.0-or-later */ -/* - * xattr_writer_flush.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "xattr_writer.h" - -static const char *hexmap = "0123456789ABCDEF"; - -static void *from_base32(const char *input, size_t *size_out) -{ - sqfs_u8 lo, hi, *out, *ptr; - size_t len; - - len = strlen(input); - *size_out = len / 2; - - out = malloc(*size_out); - if (out == NULL) - return NULL; - - ptr = out; - - while (*input != '\0') { - lo = strchr(hexmap, *(input++)) - hexmap; - hi = strchr(hexmap, *(input++)) - hexmap; - - *(ptr++) = lo | (hi << 4); - } - - return out; -} - -static sqfs_s32 write_key(sqfs_meta_writer_t *mw, const char *key, - bool value_is_ool) -{ - sqfs_xattr_entry_t kent; - int type, err; - size_t len; - - type = sqfs_get_xattr_prefix_id(key); - assert(type >= 0); - - key = strchr(key, '.'); - assert(key != NULL); - ++key; - len = strlen(key); - - if (value_is_ool) - type |= SQFS_XATTR_FLAG_OOL; - - memset(&kent, 0, sizeof(kent)); - kent.type = htole16(type); - kent.size = htole16(len); - - err = sqfs_meta_writer_append(mw, &kent, sizeof(kent)); - if (err) - return err; - - err = sqfs_meta_writer_append(mw, key, len); - if (err) - return err; - - return sizeof(kent) + len; -} - -static sqfs_s32 write_value(sqfs_meta_writer_t *mw, const char *value_str, - sqfs_u64 *value_ref_out) -{ - sqfs_xattr_value_t vent; - sqfs_u32 offset; - sqfs_u64 block; - size_t size; - void *value; - int err; - - value = from_base32(value_str, &size); - if (value == NULL) - return SQFS_ERROR_ALLOC; - - memset(&vent, 0, sizeof(vent)); - vent.size = htole32(size); - - sqfs_meta_writer_get_position(mw, &block, &offset); - *value_ref_out = (block << 16) | (offset & 0xFFFF); - - err = sqfs_meta_writer_append(mw, &vent, sizeof(vent)); - if (err) - goto fail; - - err = sqfs_meta_writer_append(mw, value, size); - if (err) - goto fail; - - free(value); - return sizeof(vent) + size; -fail: - free(value); - return err; -} - -static sqfs_s32 write_value_ool(sqfs_meta_writer_t *mw, sqfs_u64 location) -{ - sqfs_xattr_value_t vent; - sqfs_u64 ref; - int err; - - memset(&vent, 0, sizeof(vent)); - vent.size = htole32(sizeof(location)); - ref = htole64(location); - - err = sqfs_meta_writer_append(mw, &vent, sizeof(vent)); - if (err) - return err; - - err = sqfs_meta_writer_append(mw, &ref, sizeof(ref)); - if (err) - return err; - - return sizeof(vent) + sizeof(ref); -} - -static bool should_store_ool(const char *val_str, size_t refcount) -{ - if (refcount < 2) - return false; - - /* - Storing in line needs this many bytes: refcount * len - - Storing out-of-line needs this many: len + (refcount - 1) * 8 - - Out-of-line prefereable iff refcount > 1 and: - refcount * len > len + (refcount - 1) * 8 - => refcount * len - len > (refcount - 1) * 8 - => (refcount - 1) * len > (refcount - 1) * 8 - => len > 8 - */ - return (strlen(val_str) / 2) > sizeof(sqfs_u64); -} - -static int write_block_pairs(const sqfs_xattr_writer_t *xwr, - sqfs_meta_writer_t *mw, - const kv_block_desc_t *blk, - sqfs_u64 *ool_locations) -{ - const char *key_str, *value_str; - sqfs_s32 diff, total = 0; - size_t i, refcount; - sqfs_u64 ref; - - for (i = 0; i < blk->count; ++i) { - sqfs_u64 ent = ((sqfs_u64 *)xwr->kv_pairs.data)[blk->start + i]; - sqfs_u32 key_idx = GET_KEY(ent); - sqfs_u32 val_idx = GET_VALUE(ent); - - key_str = str_table_get_string(&xwr->keys, key_idx); - value_str = str_table_get_string(&xwr->values, val_idx); - - if (ool_locations[val_idx] == 0xFFFFFFFFFFFFFFFFUL) { - diff = write_key(mw, key_str, false); - if (diff < 0) - return diff; - total += diff; - - diff = write_value(mw, value_str, &ref); - if (diff < 0) - return diff; - total += diff; - - refcount = str_table_get_ref_count(&xwr->values, - val_idx); - - if (should_store_ool(value_str, refcount)) - ool_locations[val_idx] = ref; - } else { - diff = write_key(mw, key_str, true); - if (diff < 0) - return diff; - total += diff; - - diff = write_value_ool(mw, ool_locations[val_idx]); - if (diff < 0) - return diff; - total += diff; - } - } - - return total; -} - -static int write_kv_pairs(const sqfs_xattr_writer_t *xwr, - sqfs_meta_writer_t *mw) -{ - sqfs_u64 block, *ool_locations; - kv_block_desc_t *blk; - sqfs_u32 offset; - sqfs_s32 size; - size_t i; - - ool_locations = alloc_array(sizeof(ool_locations[0]), - str_table_count(&xwr->values)); - if (ool_locations == NULL) - return SQFS_ERROR_ALLOC; - - for (i = 0; i < str_table_count(&xwr->values); ++i) - ool_locations[i] = 0xFFFFFFFFFFFFFFFFUL; - - for (blk = xwr->kv_block_first; blk != NULL; blk = blk->next) { - sqfs_meta_writer_get_position(mw, &block, &offset); - blk->start_ref = (block << 16) | (offset & 0xFFFF); - - size = write_block_pairs(xwr, mw, blk, ool_locations); - if (size < 0) { - free(ool_locations); - return size; - } - - blk->size_bytes = size; - } - - free(ool_locations); - return sqfs_meta_writer_flush(mw); -} - -static int write_id_table(const sqfs_xattr_writer_t *xwr, - sqfs_meta_writer_t *mw, - sqfs_u64 *locations) -{ - sqfs_xattr_id_t id_ent; - kv_block_desc_t *blk; - sqfs_u32 offset; - sqfs_u64 block; - size_t i = 0; - int err; - - locations[i++] = 0; - - for (blk = xwr->kv_block_first; blk != NULL; blk = blk->next) { - memset(&id_ent, 0, sizeof(id_ent)); - id_ent.xattr = htole64(blk->start_ref); - id_ent.count = htole32(blk->count); - id_ent.size = htole32(blk->size_bytes); - - err = sqfs_meta_writer_append(mw, &id_ent, sizeof(id_ent)); - if (err) - return err; - - sqfs_meta_writer_get_position(mw, &block, &offset); - if (block != locations[i - 1]) - locations[i++] = block; - } - - return sqfs_meta_writer_flush(mw); -} - -static int write_location_table(const sqfs_xattr_writer_t *xwr, - sqfs_u64 kv_start, sqfs_file_t *file, - const sqfs_super_t *super, sqfs_u64 *locations, - size_t loc_count) -{ - sqfs_xattr_id_table_t idtbl; - int err; - - memset(&idtbl, 0, sizeof(idtbl)); - idtbl.xattr_table_start = htole64(kv_start); - idtbl.xattr_ids = htole32(xwr->num_blocks); - - err = file->write_at(file, super->xattr_id_table_start, - &idtbl, sizeof(idtbl)); - if (err) - return err; - - return file->write_at(file, super->xattr_id_table_start + sizeof(idtbl), - locations, sizeof(locations[0]) * loc_count); -} - -static int alloc_location_table(const sqfs_xattr_writer_t *xwr, - sqfs_u64 **tbl_out, size_t *szout) -{ - sqfs_u64 *locations; - size_t size, count; - - if (SZ_MUL_OV(xwr->num_blocks, sizeof(sqfs_xattr_id_t), &size)) - return SQFS_ERROR_OVERFLOW; - - count = size / SQFS_META_BLOCK_SIZE; - if (size % SQFS_META_BLOCK_SIZE) - ++count; - - locations = alloc_array(sizeof(sqfs_u64), count); - if (locations == NULL) - return SQFS_ERROR_ALLOC; - - *tbl_out = locations; - *szout = count; - return 0; -} - -int sqfs_xattr_writer_flush(const sqfs_xattr_writer_t *xwr, sqfs_file_t *file, - sqfs_super_t *super, sqfs_compressor_t *cmp) -{ - sqfs_u64 *locations = NULL, kv_start, id_start; - sqfs_meta_writer_t *mw; - size_t i, count; - int err; - - if (xwr->kv_pairs.used == 0 || xwr->num_blocks == 0) { - super->xattr_id_table_start = 0xFFFFFFFFFFFFFFFFUL; - super->flags |= SQFS_FLAG_NO_XATTRS; - return 0; - } - - mw = sqfs_meta_writer_create(file, cmp, 0); - if (mw == NULL) - return SQFS_ERROR_ALLOC; - - kv_start = file->get_size(file); - err = write_kv_pairs(xwr, mw); - if (err) - goto out; - - sqfs_meta_writer_reset(mw); - - id_start = file->get_size(file); - err = alloc_location_table(xwr, &locations, &count); - if (err) - goto out; - - err = write_id_table(xwr, mw, locations); - if (err) - goto out; - - super->xattr_id_table_start = file->get_size(file); - super->flags &= ~SQFS_FLAG_NO_XATTRS; - - for (i = 0; i < count; ++i) - locations[i] = htole64(locations[i] + id_start); - - err = write_location_table(xwr, kv_start, file, super, - locations, count); -out: - free(locations); - sqfs_drop(mw); - return err; -} diff --git a/lib/sqfs/xattr/xattr_writer_record.c b/lib/sqfs/xattr/xattr_writer_record.c deleted file mode 100644 index 81bbf6b..0000000 --- a/lib/sqfs/xattr/xattr_writer_record.c +++ /dev/null @@ -1,145 +0,0 @@ -/* SPDX-License-Identifier: LGPL-3.0-or-later */ -/* - * xattr_writer_record.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "xattr_writer.h" - -static const char *hexmap = "0123456789ABCDEF"; - -static char *to_base32(const void *input, size_t size) -{ - const sqfs_u8 *in = input; - char *out, *ptr; - size_t i; - - out = malloc(2 * size + 1); - if (out == NULL) - return NULL; - - ptr = out; - - for (i = 0; i < size; ++i) { - *(ptr++) = hexmap[ in[i] & 0x0F]; - *(ptr++) = hexmap[(in[i] >> 4) & 0x0F]; - } - - *ptr = '\0'; - return out; -} - -static int compare_u64(const void *a, const void *b) -{ - sqfs_u64 lhs = *((const sqfs_u64 *)a); - sqfs_u64 rhs = *((const sqfs_u64 *)b); - - return (lhs < rhs ? -1 : (lhs > rhs ? 1 : 0)); -} - -int sqfs_xattr_writer_begin(sqfs_xattr_writer_t *xwr, sqfs_u32 flags) -{ - if (flags != 0) - return SQFS_ERROR_UNSUPPORTED; - - xwr->kv_start = xwr->kv_pairs.used; - return 0; -} - -int sqfs_xattr_writer_add(sqfs_xattr_writer_t *xwr, const char *key, - const void *value, size_t size) -{ - size_t i, key_index, old_value_index, value_index; - sqfs_u64 kv_pair; - char *value_str; - int err; - - if (sqfs_get_xattr_prefix_id(key) < 0) - return SQFS_ERROR_UNSUPPORTED; - - err = str_table_get_index(&xwr->keys, key, &key_index); - if (err) - return err; - - value_str = to_base32(value, size); - if (value_str == NULL) - return SQFS_ERROR_ALLOC; - - err = str_table_get_index(&xwr->values, value_str, &value_index); - free(value_str); - if (err) - return err; - - str_table_add_ref(&xwr->values, value_index); - - if (sizeof(size_t) > sizeof(sqfs_u32)) { - if (key_index > 0x0FFFFFFFFUL || value_index > 0x0FFFFFFFFUL) - return SQFS_ERROR_OVERFLOW; - } - - kv_pair = MK_PAIR(key_index, value_index); - - for (i = xwr->kv_start; i < xwr->kv_pairs.used; ++i) { - sqfs_u64 ent = ((sqfs_u64 *)xwr->kv_pairs.data)[i]; - - if (ent == kv_pair) - return 0; - - if (GET_KEY(ent) == key_index) { - old_value_index = GET_VALUE(ent); - - str_table_del_ref(&xwr->values, old_value_index); - - ((sqfs_u64 *)xwr->kv_pairs.data)[i] = kv_pair; - return 0; - } - } - - return array_append(&xwr->kv_pairs, &kv_pair); -} - -int sqfs_xattr_writer_end(sqfs_xattr_writer_t *xwr, sqfs_u32 *out) -{ - kv_block_desc_t blk; - rbtree_node_t *n; - sqfs_u32 index; - int ret; - - memset(&blk, 0, sizeof(blk)); - blk.start = xwr->kv_start; - blk.count = xwr->kv_pairs.used - xwr->kv_start; - - if (blk.count == 0) { - *out = 0xFFFFFFFF; - return 0; - } - - array_sort_range(&xwr->kv_pairs, blk.start, blk.count, compare_u64); - - n = rbtree_lookup(&xwr->kv_block_tree, &blk); - - if (n != NULL) { - index = *((sqfs_u32 *)rbtree_node_value(n)); - xwr->kv_pairs.used = xwr->kv_start; - } else { - index = xwr->num_blocks; - - ret = rbtree_insert(&xwr->kv_block_tree, &blk, &index); - if (ret != 0) - return ret; - - xwr->num_blocks += 1; - n = rbtree_lookup(&xwr->kv_block_tree, &blk); - - if (xwr->kv_block_last == NULL) { - xwr->kv_block_first = rbtree_node_key(n); - xwr->kv_block_last = xwr->kv_block_first; - } else { - xwr->kv_block_last->next = rbtree_node_key(n); - xwr->kv_block_last = xwr->kv_block_last->next; - } - } - - *out = index; - return 0; -} diff --git a/lib/tar/Makemodule.am b/lib/tar/Makemodule.am index 7f93d13..1db02f1 100644 --- a/lib/tar/Makemodule.am +++ b/lib/tar/Makemodule.am @@ -1,11 +1,8 @@ -libtar_a_SOURCES = lib/tar/read_header.c lib/tar/write_header.c -libtar_a_SOURCES += lib/tar/number.c lib/tar/checksum.c lib/tar/cleanup.c -libtar_a_SOURCES += lib/tar/read_sparse_map_old.c -libtar_a_SOURCES += lib/tar/internal.h -libtar_a_SOURCES += lib/tar/padd_file.c lib/tar/record_to_memory.c -libtar_a_SOURCES += lib/tar/pax_header.c lib/tar/read_sparse_map_new.c -libtar_a_SOURCES += include/tar/tar.h include/tar/format.h -libtar_a_CFLAGS = $(AM_CFLAGS) -libtar_a_CPPFLAGS = $(AM_CPPFLAGS) +libtar_a_SOURCES = lib/tar/src/read_header.c lib/tar/src/write_header.c \ + lib/tar/src/number.c lib/tar/src/checksum.c lib/tar/src/cleanup.c \ + lib/tar/src/read_sparse_map_old.c lib/tar/src/internal.h \ + lib/tar/src/padd_file.c lib/tar/src/record_to_memory.c \ + lib/tar/src/pax_header.c lib/tar/src/read_sparse_map_new.c \ + include/tar/tar.h include/tar/format.h noinst_LIBRARIES += libtar.a diff --git a/lib/tar/checksum.c b/lib/tar/checksum.c deleted file mode 100644 index 6541373..0000000 --- a/lib/tar/checksum.c +++ /dev/null @@ -1,48 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * checksum.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "config.h" - -#include "tar/format.h" - -#include - -static unsigned int get_checksum(const tar_header_t *hdr) -{ - const unsigned char *header_start = (const unsigned char *)hdr; - const unsigned char *chksum_start = (const unsigned char *)hdr->chksum; - const unsigned char *header_end = header_start + sizeof(*hdr); - const unsigned char *chksum_end = chksum_start + sizeof(hdr->chksum); - const unsigned char *p; - unsigned int chksum = 0; - - for (p = header_start; p < chksum_start; p++) - chksum += *p; - for (; p < chksum_end; p++) - chksum += ' '; - for (; p < header_end; p++) - chksum += *p; - return chksum; -} - -void update_checksum(tar_header_t *hdr) -{ - unsigned int chksum = get_checksum(hdr); - - sprintf(hdr->chksum, "%06o", chksum); - hdr->chksum[6] = '\0'; - hdr->chksum[7] = ' '; -} - -bool is_checksum_valid(const tar_header_t *hdr) -{ - unsigned int calculated_chksum = get_checksum(hdr); - sqfs_u64 read_chksum; - - if (read_octal(hdr->chksum, sizeof(hdr->chksum), &read_chksum)) - return 0; - return read_chksum == calculated_chksum; -} diff --git a/lib/tar/cleanup.c b/lib/tar/cleanup.c deleted file mode 100644 index 9f33336..0000000 --- a/lib/tar/cleanup.c +++ /dev/null @@ -1,42 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * cleanup.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "config.h" - -#include "internal.h" -#include -#include - -void free_sparse_list(sparse_map_t *sparse) -{ - sparse_map_t *old; - - while (sparse != NULL) { - old = sparse; - sparse = sparse->next; - free(old); - } -} - -void free_xattr_list(tar_xattr_t *list) -{ - tar_xattr_t *old; - - while (list != NULL) { - old = list; - list = list->next; - free(old); - } -} - -void clear_header(tar_header_decoded_t *hdr) -{ - free_xattr_list(hdr->xattr); - free_sparse_list(hdr->sparse); - free(hdr->name); - free(hdr->link_target); - memset(hdr, 0, sizeof(*hdr)); -} diff --git a/lib/tar/internal.h b/lib/tar/internal.h deleted file mode 100644 index c5483fe..0000000 --- a/lib/tar/internal.h +++ /dev/null @@ -1,46 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * internal.h - * - * Copyright (C) 2019 David Oberhollenzer - */ -#ifndef INTERNAL_H -#define INTERNAL_H - -#include "config.h" - -#include "tar/tar.h" -#include "tar/format.h" -#include "util/util.h" - -enum { - PAX_SIZE = 0x001, - PAX_UID = 0x002, - PAX_GID = 0x004, - PAX_DEV_MAJ = 0x008, - PAX_DEV_MIN = 0x010, - PAX_NAME = 0x020, - PAX_SLINK_TARGET = 0x040, - PAX_MTIME = 0x100, - PAX_SPARSE_SIZE = 0x400, - - PAX_SPARSE_GNU_1_X = 0x800, -}; - -enum { - ETV_UNKNOWN = 0, - ETV_V7_UNIX, - ETV_PRE_POSIX, - ETV_POSIX, -}; - -sparse_map_t *read_gnu_old_sparse(istream_t *fp, tar_header_t *hdr); - -sparse_map_t *read_gnu_new_sparse(istream_t *fp, tar_header_decoded_t *out); - -char *record_to_memory(istream_t *fp, size_t size); - -int read_pax_header(istream_t *fp, sqfs_u64 entsize, unsigned int *set_by_pax, - tar_header_decoded_t *out); - -#endif /* INTERNAL_H */ diff --git a/lib/tar/number.c b/lib/tar/number.c deleted file mode 100644 index 2f179df..0000000 --- a/lib/tar/number.c +++ /dev/null @@ -1,79 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * number.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "config.h" - -#include "tar/format.h" - -#include -#include - -int read_octal(const char *str, int digits, sqfs_u64 *out) -{ - sqfs_u64 result = 0; - - while (digits > 0 && isspace(*str)) { - ++str; - --digits; - } - - while (digits > 0 && *str >= '0' && *str <= '7') { - if (result > 0x1FFFFFFFFFFFFFFFUL) { - fputs("numeric overflow parsing tar header\n", stderr); - return -1; - } - - result = (result << 3) | (*(str++) - '0'); - --digits; - } - - *out = result; - return 0; -} - -static int read_binary(const char *str, int digits, sqfs_u64 *out) -{ - sqfs_u64 x, ov, result = 0; - bool first = true; - - while (digits > 0) { - x = *((const unsigned char *)str++); - --digits; - - if (first) { - first = false; - if (x == 0xFF) { - result = 0xFFFFFFFFFFFFFFFFUL; - } else { - x &= 0x7F; - result = 0; - if (digits > 7 && x != 0) - goto fail_ov; - } - } - - ov = (result >> 56) & 0xFF; - - if (ov != 0 && ov != 0xFF) - goto fail_ov; - - result = (result << 8) | x; - } - - *out = result; - return 0; -fail_ov: - fputs("numeric overflow parsing tar header\n", stderr); - return -1; -} - -int read_number(const char *str, int digits, sqfs_u64 *out) -{ - if (*((const unsigned char *)str) & 0x80) - return read_binary(str, digits, out); - - return read_octal(str, digits, out); -} diff --git a/lib/tar/padd_file.c b/lib/tar/padd_file.c deleted file mode 100644 index 053ff1e..0000000 --- a/lib/tar/padd_file.c +++ /dev/null @@ -1,19 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * padd_file.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "config.h" -#include "tar/tar.h" -#include "tar/format.h" - -int padd_file(ostream_t *fp, sqfs_u64 size) -{ - size_t padd_sz = size % TAR_RECORD_SIZE; - - if (padd_sz == 0) - return 0; - - return ostream_append_sparse(fp, TAR_RECORD_SIZE - padd_sz); -} diff --git a/lib/tar/pax_header.c b/lib/tar/pax_header.c deleted file mode 100644 index b61aab6..0000000 --- a/lib/tar/pax_header.c +++ /dev/null @@ -1,402 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * pax_header.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "config.h" - -#include "internal.h" -#include -#include -#include - -static int pax_read_decimal(const char *str, sqfs_u64 *out) -{ - sqfs_u64 result = 0; - - while (*str >= '0' && *str <= '9') { - if (result > 0xFFFFFFFFFFFFFFFFUL / 10) { - fputs("numeric overflow parsing pax header\n", stderr); - return -1; - } - - result = (result * 10) + (*(str++) - '0'); - } - - *out = result; - return 0; -} - -static void urldecode(char *str) -{ - unsigned char *out = (unsigned char *)str; - char *in = str; - - while (*in != '\0') { - sqfs_u8 x = *(in++); - - if (x == '%' && isxdigit(in[0]) && isxdigit(in[1])) { - hex_decode(in, 2, &x, 1); - in += 2; - } - - *(out++) = x; - } - - *out = '\0'; -} - -static int pax_uid(tar_header_decoded_t *out, sqfs_u64 id) -{ - out->uid = id; - return 0; -} - -static int pax_gid(tar_header_decoded_t *out, sqfs_u64 id) -{ - out->gid = id; - return 0; -} - -static int pax_size(tar_header_decoded_t *out, sqfs_u64 size) -{ - out->record_size = size; - return 0; -} - -static int pax_mtime(tar_header_decoded_t *out, sqfs_s64 mtime) -{ - out->mtime = mtime; - return 0; -} - -static int pax_rsize(tar_header_decoded_t *out, sqfs_u64 size) -{ - out->actual_size = size; - return 0; -} - -static int pax_path(tar_header_decoded_t *out, char *path) -{ - free(out->name); - out->name = path; - return 0; -} - -static int pax_slink(tar_header_decoded_t *out, char *path) -{ - free(out->link_target); - out->link_target = path; - return 0; -} - -static int pax_sparse_map(tar_header_decoded_t *out, const char *line) -{ - sparse_map_t *last = NULL, *list = NULL, *ent = NULL; - - free_sparse_list(out->sparse); - out->sparse = NULL; - - do { - ent = calloc(1, sizeof(*ent)); - if (ent == NULL) - goto fail_errno; - - if (pax_read_decimal(line, &ent->offset)) - goto fail_format; - - while (isdigit(*line)) - ++line; - - if (*(line++) != ',') - goto fail_format; - - if (pax_read_decimal(line, &ent->count)) - goto fail_format; - - while (isdigit(*line)) - ++line; - - if (last == NULL) { - list = last = ent; - } else { - last->next = ent; - last = ent; - } - } while (*(line++) == ','); - - out->sparse = list; - return 0; -fail_errno: - perror("parsing GNU pax sparse file record"); - goto fail; -fail_format: - fputs("malformed GNU pax sparse file record\n", stderr); - goto fail; -fail: - free_sparse_list(list); - free(ent); - return -1; -} - -static int pax_xattr_schily(tar_header_decoded_t *out, - tar_xattr_t *xattr) -{ - xattr->next = out->xattr; - out->xattr = xattr; - return 0; -} - -static int pax_xattr_libarchive(tar_header_decoded_t *out, - tar_xattr_t *xattr) -{ - int ret; - - ret = base64_decode((const char *)xattr->value, xattr->value_len, - xattr->value, &xattr->value_len); - if (ret) - return -1; - - urldecode(xattr->key); - - xattr->value[xattr->value_len] = '\0'; - xattr->next = out->xattr; - out->xattr = xattr; - return 0; -} - -enum { - PAX_TYPE_SINT = 0, - PAX_TYPE_UINT, - PAX_TYPE_STRING, - PAX_TYPE_CONST_STRING, - PAX_TYPE_PREFIXED_XATTR, - PAX_TYPE_IGNORE, -}; - -static const struct pax_handler_t { - const char *name; - int flag; - int type; - union { - int (*sint)(tar_header_decoded_t *out, sqfs_s64 sval); - int (*uint)(tar_header_decoded_t *out, sqfs_u64 uval); - int (*str)(tar_header_decoded_t *out, char *str); - int (*cstr)(tar_header_decoded_t *out, const char *str); - int (*xattr)(tar_header_decoded_t *out, tar_xattr_t *xattr); - } cb; -} pax_fields[] = { - { "uid", PAX_UID, PAX_TYPE_UINT, { .uint = pax_uid } }, - { "gid", PAX_GID, PAX_TYPE_UINT, { .uint = pax_gid } }, - { "path", PAX_NAME, PAX_TYPE_STRING, { .str = pax_path } }, - { "size", PAX_SIZE, PAX_TYPE_UINT, { .uint = pax_size } }, - { "linkpath", PAX_SLINK_TARGET, PAX_TYPE_STRING, { .str = pax_slink } }, - { "mtime", PAX_MTIME, PAX_TYPE_SINT, { .sint = pax_mtime } }, - { "GNU.sparse.name", PAX_NAME, PAX_TYPE_STRING, { .str = pax_path } }, - { "GNU.sparse.size", PAX_SPARSE_SIZE, PAX_TYPE_UINT, - {.uint = pax_rsize} }, - { "GNU.sparse.realsize", PAX_SPARSE_SIZE, PAX_TYPE_UINT, - {.uint = pax_rsize} }, - { "GNU.sparse.major", PAX_SPARSE_GNU_1_X, PAX_TYPE_IGNORE, - { .str = NULL } }, - { "GNU.sparse.minor", PAX_SPARSE_GNU_1_X, PAX_TYPE_IGNORE, - { .str = NULL }}, - { "SCHILY.xattr", 0, PAX_TYPE_PREFIXED_XATTR, - { .xattr = pax_xattr_schily } }, - { "LIBARCHIVE.xattr", 0, PAX_TYPE_PREFIXED_XATTR, - { .xattr = pax_xattr_libarchive } }, - { "GNU.sparse.map", 0, PAX_TYPE_CONST_STRING, - { .cstr = pax_sparse_map } }, -}; - -static const struct pax_handler_t *find_handler(const char *key) -{ - size_t i, fieldlen; - - for (i = 0; i < sizeof(pax_fields) / sizeof(pax_fields[0]); ++i) { - if (pax_fields[i].type == PAX_TYPE_PREFIXED_XATTR) { - fieldlen = strlen(pax_fields[i].name); - - if (strncmp(key, pax_fields[i].name, fieldlen)) - continue; - - if (key[fieldlen] != '.') - continue; - - return pax_fields + i; - } - - if (!strcmp(key, pax_fields[i].name)) - return pax_fields + i; - } - - return NULL; -} - -static tar_xattr_t *mkxattr(const char *key, - const char *value, size_t valuelen) -{ - size_t keylen = strlen(key); - tar_xattr_t *xattr; - - xattr = calloc(1, sizeof(*xattr) + keylen + 1 + valuelen + 1); - if (xattr == NULL) - return NULL; - - xattr->key = xattr->data; - memcpy(xattr->key, key, keylen); - xattr->key[keylen] = '\0'; - - xattr->value = (sqfs_u8 *)xattr->key + keylen + 1; - memcpy(xattr->value, value, valuelen); - xattr->value[valuelen] = '\0'; - - xattr->value_len = valuelen; - return xattr; -} - -static int apply_handler(tar_header_decoded_t *out, - const struct pax_handler_t *field, const char *key, - const char *value, size_t valuelen) -{ - tar_xattr_t *xattr; - sqfs_s64 s64val; - sqfs_u64 uval; - char *copy; - - switch (field->type) { - case PAX_TYPE_SINT: - if (value[0] == '-') { - if (pax_read_decimal(value + 1, &uval)) - return -1; - s64val = -((sqfs_s64)uval); - } else { - if (pax_read_decimal(value, &uval)) - return -1; - s64val = (sqfs_s64)uval; - } - return field->cb.sint(out, s64val); - case PAX_TYPE_UINT: - if (pax_read_decimal(value, &uval)) - return -1; - return field->cb.uint(out, uval); - case PAX_TYPE_CONST_STRING: - return field->cb.cstr(out, value); - case PAX_TYPE_STRING: - copy = strdup(value); - if (copy == NULL) { - perror("processing pax header"); - return -1; - } - if (field->cb.str(out, copy)) { - free(copy); - return -1; - } - break; - case PAX_TYPE_PREFIXED_XATTR: - xattr = mkxattr(key + strlen(field->name) + 1, - value, valuelen); - if (xattr == NULL) { - perror("reading pax xattr field"); - return -1; - } - if (field->cb.xattr(out, xattr)) { - free(xattr); - return -1; - } - break; - default: - break; - } - - return 0; -} - -int read_pax_header(istream_t *fp, sqfs_u64 entsize, unsigned int *set_by_pax, - tar_header_decoded_t *out) -{ - char *buffer, *line, *key, *ptr, *value, *end; - sparse_map_t *sparse_last = NULL, *sparse; - sqfs_u64 offset = 0, num_bytes = 0; - const struct pax_handler_t *field; - long len; - - buffer = record_to_memory(fp, entsize); - if (buffer == NULL) - return -1; - - end = buffer + entsize; - - for (line = buffer; line < end; line += len) { - len = strtol(line, &ptr, 10); - if (ptr == line || !isspace(*ptr) || len <= 0) - goto fail_malformed; - - if (len > (end - line)) - goto fail_ov; - - line[len - 1] = '\0'; - - while (ptr < end && isspace(*ptr)) - ++ptr; - - if (ptr >= end || (ptr - line) >= len) - goto fail_malformed; - - key = ptr; - - while (*ptr != '\0' && *ptr != '=') - ++ptr; - - if (ptr == key || *ptr != '=') - goto fail_malformed; - - *(ptr++) = '\0'; - value = ptr; - - field = find_handler(key); - - if (field != NULL) { - if (apply_handler(out, field, key, value, - len - (value - line) - 1)) { - goto fail; - } - - *set_by_pax |= field->flag; - } else if (!strcmp(key, "GNU.sparse.offset")) { - if (pax_read_decimal(value, &offset)) - goto fail; - } else if (!strcmp(key, "GNU.sparse.numbytes")) { - if (pax_read_decimal(value, &num_bytes)) - goto fail; - sparse = calloc(1, sizeof(*sparse)); - if (sparse == NULL) - goto fail_errno; - sparse->offset = offset; - sparse->count = num_bytes; - if (sparse_last == NULL) { - free_sparse_list(out->sparse); - out->sparse = sparse_last = sparse; - } else { - sparse_last->next = sparse; - sparse_last = sparse; - } - } - } - - free(buffer); - return 0; -fail_malformed: - fputs("Found a malformed PAX header.\n", stderr); - goto fail; -fail_ov: - fputs("Numeric overflow in PAX header.\n", stderr); - goto fail; -fail_errno: - perror("reading pax header"); - goto fail; -fail: - free(buffer); - return -1; -} diff --git a/lib/tar/read_header.c b/lib/tar/read_header.c deleted file mode 100644 index ea4873b..0000000 --- a/lib/tar/read_header.c +++ /dev/null @@ -1,304 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * read_header.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "config.h" - -#include "internal.h" -#include -#include - -static bool is_zero_block(const tar_header_t *hdr) -{ - const unsigned char *ptr = (const unsigned char *)hdr; - - return ptr[0] == '\0' && memcmp(ptr, ptr + 1, sizeof(*hdr) - 1) == 0; -} - -static int check_version(const tar_header_t *hdr) -{ - char buffer[sizeof(hdr->magic) + sizeof(hdr->version)]; - - memset(buffer, '\0', sizeof(buffer)); - if (memcmp(hdr->magic, buffer, sizeof(hdr->magic)) == 0 && - memcmp(hdr->version, buffer, sizeof(hdr->version)) == 0) - return ETV_V7_UNIX; - - if (memcmp(hdr->magic, TAR_MAGIC, sizeof(hdr->magic)) == 0 && - memcmp(hdr->version, TAR_VERSION, sizeof(hdr->version)) == 0) - return ETV_POSIX; - - if (memcmp(hdr->magic, TAR_MAGIC_OLD, sizeof(hdr->magic)) == 0 && - memcmp(hdr->version, TAR_VERSION_OLD, sizeof(hdr->version)) == 0) - return ETV_PRE_POSIX; - - return ETV_UNKNOWN; -} - -static int decode_header(const tar_header_t *hdr, unsigned int set_by_pax, - tar_header_decoded_t *out, int version) -{ - size_t len1, len2; - sqfs_u64 field; - - if (!(set_by_pax & PAX_NAME)) { - if (hdr->tail.posix.prefix[0] != '\0' && - version == ETV_POSIX) { - len1 = strnlen(hdr->name, sizeof(hdr->name)); - len2 = strnlen(hdr->tail.posix.prefix, - sizeof(hdr->tail.posix.prefix)); - - out->name = malloc(len1 + 1 + len2 + 1); - - if (out->name != NULL) { - memcpy(out->name, hdr->tail.posix.prefix, len2); - out->name[len2] = '/'; - memcpy(out->name + len2 + 1, hdr->name, len1); - out->name[len1 + 1 + len2] = '\0'; - } - } else { - out->name = strndup(hdr->name, sizeof(hdr->name)); - } - - if (out->name == NULL) { - perror("decoding filename"); - return -1; - } - } - - if (!(set_by_pax & PAX_SIZE)) { - if (read_number(hdr->size, sizeof(hdr->size), &out->record_size)) - return -1; - } - - if (!(set_by_pax & PAX_UID)) { - if (read_number(hdr->uid, sizeof(hdr->uid), &field)) - return -1; - out->uid = field; - } - - if (!(set_by_pax & PAX_GID)) { - if (read_number(hdr->gid, sizeof(hdr->gid), &field)) - return -1; - out->gid = field; - } - - if (!(set_by_pax & PAX_DEV_MAJ)) { - if (read_number(hdr->devmajor, sizeof(hdr->devmajor), &field)) - return -1; - - out->devno = makedev(field, minor(out->devno)); - } - - if (!(set_by_pax & PAX_DEV_MIN)) { - if (read_number(hdr->devminor, sizeof(hdr->devminor), &field)) - return -1; - - out->devno = makedev(major(out->devno), field); - } - - if (!(set_by_pax & PAX_MTIME)) { - if (read_number(hdr->mtime, sizeof(hdr->mtime), &field)) - return -1; - if (field & 0x8000000000000000UL) { - field = ~field + 1; - out->mtime = -((sqfs_s64)field); - } else { - out->mtime = field; - } - } - - if (read_octal(hdr->mode, sizeof(hdr->mode), &field)) - return -1; - - out->mode = field & 07777; - - if (hdr->typeflag == TAR_TYPE_LINK || - hdr->typeflag == TAR_TYPE_SLINK) { - if (!(set_by_pax & PAX_SLINK_TARGET)) { - out->link_target = strndup(hdr->linkname, - sizeof(hdr->linkname)); - if (out->link_target == NULL) { - perror("decoding symlink target"); - return -1; - } - } - } - - out->unknown_record = false; - - switch (hdr->typeflag) { - case '\0': - case TAR_TYPE_FILE: - case TAR_TYPE_GNU_SPARSE: - out->mode |= S_IFREG; - break; - case TAR_TYPE_LINK: - out->is_hard_link = true; - break; - case TAR_TYPE_SLINK: - out->mode = S_IFLNK | 0777; - break; - case TAR_TYPE_CHARDEV: - out->mode |= S_IFCHR; - break; - case TAR_TYPE_BLOCKDEV: - out->mode |= S_IFBLK; - break; - case TAR_TYPE_DIR: - out->mode |= S_IFDIR; - break; - case TAR_TYPE_FIFO: - out->mode |= S_IFIFO; - break; - default: - out->unknown_record = true; - break; - } - - return 0; -} - -int read_header(istream_t *fp, tar_header_decoded_t *out) -{ - unsigned int set_by_pax = 0; - bool prev_was_zero = false; - sqfs_u64 pax_size; - tar_header_t hdr; - int version, ret; - - memset(out, 0, sizeof(*out)); - - for (;;) { - ret = istream_read(fp, &hdr, sizeof(hdr)); - if (ret < 0) - goto fail; - - if ((size_t)ret < sizeof(hdr)) - goto out_eof; - - if (is_zero_block(&hdr)) { - if (prev_was_zero) - goto out_eof; - prev_was_zero = true; - continue; - } - - prev_was_zero = false; - version = check_version(&hdr); - - if (version == ETV_UNKNOWN) - goto fail_magic; - - if (!is_checksum_valid(&hdr)) - goto fail_chksum; - - switch (hdr.typeflag) { - case TAR_TYPE_GNU_SLINK: - if (read_number(hdr.size, sizeof(hdr.size), &pax_size)) - goto fail; - if (pax_size < 1 || pax_size > TAR_MAX_SYMLINK_LEN) - goto fail_slink_len; - free(out->link_target); - out->link_target = record_to_memory(fp, pax_size); - if (out->link_target == NULL) - goto fail; - set_by_pax |= PAX_SLINK_TARGET; - continue; - case TAR_TYPE_GNU_PATH: - if (read_number(hdr.size, sizeof(hdr.size), &pax_size)) - goto fail; - if (pax_size < 1 || pax_size > TAR_MAX_PATH_LEN) - goto fail_path_len; - free(out->name); - out->name = record_to_memory(fp, pax_size); - if (out->name == NULL) - goto fail; - set_by_pax |= PAX_NAME; - continue; - case TAR_TYPE_PAX_GLOBAL: - if (read_number(hdr.size, sizeof(hdr.size), &pax_size)) - goto fail; - skip_entry(fp, pax_size); - continue; - case TAR_TYPE_PAX: - clear_header(out); - if (read_number(hdr.size, sizeof(hdr.size), &pax_size)) - goto fail; - if (pax_size < 1 || pax_size > TAR_MAX_PAX_LEN) - goto fail_pax_len; - set_by_pax = 0; - if (read_pax_header(fp, pax_size, &set_by_pax, out)) - goto fail; - continue; - case TAR_TYPE_GNU_SPARSE: - free_sparse_list(out->sparse); - out->sparse = read_gnu_old_sparse(fp, &hdr); - if (out->sparse == NULL) - goto fail; - if (read_number(hdr.tail.gnu.realsize, - sizeof(hdr.tail.gnu.realsize), - &out->actual_size)) - goto fail; - break; - default: - break; - } - break; - } - - if (decode_header(&hdr, set_by_pax, out, version)) - goto fail; - - if (set_by_pax & PAX_SPARSE_GNU_1_X) { - free_sparse_list(out->sparse); - out->sparse = read_gnu_new_sparse(fp, out); - if (out->sparse == NULL) - goto fail; - } - - if (out->sparse == NULL) - out->actual_size = out->record_size; - - return 0; -out_eof: - clear_header(out); - return 1; -fail_slink_len: - fprintf(stderr, "rejecting GNU symlink header with size %lu\n", - (unsigned long)pax_size); - goto fail; -fail_path_len: - fprintf(stderr, "rejecting GNU long path header with size %lu\n", - (unsigned long)pax_size); - goto fail; -fail_pax_len: - fprintf(stderr, "rejecting PAX header with size %lu\n", - (unsigned long)pax_size); - goto fail; -fail_magic: - fputs("input is not a ustar tar archive!\n", stderr); - goto fail; -fail_chksum: - fputs("invalid tar header checksum!\n", stderr); - goto fail; -fail: - clear_header(out); - return -1; -} - -int skip_padding(istream_t *fp, sqfs_u64 size) -{ - size_t tail = size % 512; - - return tail ? istream_skip(fp, 512 - tail) : 0; -} - -int skip_entry(istream_t *fp, sqfs_u64 size) -{ - size_t tail = size % 512; - - return istream_skip(fp, tail ? (size + 512 - tail) : size); -} diff --git a/lib/tar/read_sparse_map_new.c b/lib/tar/read_sparse_map_new.c deleted file mode 100644 index de1b6a4..0000000 --- a/lib/tar/read_sparse_map_new.c +++ /dev/null @@ -1,115 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * read_sparse_map_new.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "config.h" -#include "internal.h" - -#include -#include -#include - -static int decode(const char *str, size_t len, size_t *out) -{ - size_t count = 0; - - *out = 0; - - while (count < len && isdigit(*str)) { - if (SZ_MUL_OV(*out, 10, out)) - return -1; - if (SZ_ADD_OV(*out, (*(str++) - '0'), out)) - return -1; - ++count; - } - - if (count == 0 || count == len) - return 0; - - return (*str == '\n') ? ((int)count + 1) : -1; -} - -sparse_map_t *read_gnu_new_sparse(istream_t *fp, tar_header_decoded_t *out) -{ - sparse_map_t *last = NULL, *list = NULL, *ent = NULL; - size_t i, count, value; - char buffer[1024]; - int diff, ret; - - if (out->record_size < 512) - goto fail_format; - - ret = istream_read(fp, buffer, 512); - if (ret < 0) - goto fail; - - if (ret < 512) - goto fail_format; - - diff = decode(buffer, 512, &count); - if (diff <= 0) - goto fail_format; - - out->record_size -= 512; - - if (count == 0 || count > TAR_MAX_SPARSE_ENT) - goto fail_format; - - for (i = 0; i < (count * 2); ++i) { - ret = decode(buffer + diff, 512 - diff, &value); - if (ret < 0) - goto fail_format; - - if (ret > 0) { - diff += ret; - } else { - if (out->record_size < 512) - goto fail_format; - - ret = istream_read(fp, buffer + 512, 512); - if (ret < 0) - goto fail; - - if (ret < 512) - goto fail_format; - - ret = decode(buffer + diff, 1024 - diff, &value); - if (ret <= 0) - goto fail_format; - - memcpy(buffer, buffer + 512, 512); - diff = diff + ret - 512; - out->record_size -= 512; - } - - if ((i & 0x01) == 0) { - ent = calloc(1, sizeof(*ent)); - if (ent == NULL) - goto fail_errno; - - if (list == NULL) { - list = last = ent; - } else { - last->next = ent; - last = ent; - } - - ent->offset = value; - } else { - ent->count = value; - } - } - - return list; -fail_errno: - perror("parsing GNU 1.0 style sparse file record"); - goto fail; -fail_format: - fputs("Malformed GNU 1.0 style sparse file map.\n", stderr); - goto fail; -fail: - free_sparse_list(list); - return NULL; -} diff --git a/lib/tar/read_sparse_map_old.c b/lib/tar/read_sparse_map_old.c deleted file mode 100644 index 3dd3300..0000000 --- a/lib/tar/read_sparse_map_old.c +++ /dev/null @@ -1,99 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * read_sparse_map_old.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "config.h" -#include "internal.h" - -#include -#include - -sparse_map_t *read_gnu_old_sparse(istream_t *fp, tar_header_t *hdr) -{ - sparse_map_t *list = NULL, *end = NULL, *node; - gnu_sparse_t sph; - sqfs_u64 off, sz; - int i, ret; - - for (i = 0; i < 4; ++i) { - if (!isdigit(hdr->tail.gnu.sparse[i].offset[0])) - break; - if (!isdigit(hdr->tail.gnu.sparse[i].numbytes[0])) - break; - - if (read_octal(hdr->tail.gnu.sparse[i].offset, - sizeof(hdr->tail.gnu.sparse[i].offset), &off)) - goto fail; - if (read_octal(hdr->tail.gnu.sparse[i].numbytes, - sizeof(hdr->tail.gnu.sparse[i].numbytes), &sz)) - goto fail; - - node = calloc(1, sizeof(*node)); - if (node == NULL) - goto fail_errno; - - node->offset = off; - node->count = sz; - - if (list == NULL) { - list = end = node; - } else { - end->next = node; - end = node; - } - } - - if (hdr->tail.gnu.isextended == 0) - return list; - - do { - ret = istream_read(fp, &sph, sizeof(sph)); - if (ret < 0) - goto fail; - - if ((size_t)ret < sizeof(sph)) { - fputs("reading GNU sparse header: " - "unexpected end-of-file\n", - stderr); - goto fail; - } - - for (i = 0; i < 21; ++i) { - if (!isdigit(sph.sparse[i].offset[0])) - break; - if (!isdigit(sph.sparse[i].numbytes[0])) - break; - - if (read_octal(sph.sparse[i].offset, - sizeof(sph.sparse[i].offset), &off)) - goto fail; - if (read_octal(sph.sparse[i].numbytes, - sizeof(sph.sparse[i].numbytes), &sz)) - goto fail; - - node = calloc(1, sizeof(*node)); - if (node == NULL) - goto fail_errno; - - node->offset = off; - node->count = sz; - - if (list == NULL) { - list = end = node; - } else { - end->next = node; - end = node; - } - } - } while (sph.isextended != 0); - - return list; -fail_errno: - perror("parsing GNU sparse header"); - goto fail; -fail: - free_sparse_list(list); - return NULL; -} diff --git a/lib/tar/record_to_memory.c b/lib/tar/record_to_memory.c deleted file mode 100644 index ba422de..0000000 --- a/lib/tar/record_to_memory.c +++ /dev/null @@ -1,41 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * record_to_memory.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "config.h" - -#include "tar/tar.h" -#include "internal.h" -#include - -char *record_to_memory(istream_t *fp, size_t size) -{ - char *buffer = malloc(size + 1); - int ret; - - if (buffer == NULL) - goto fail_errno; - - ret = istream_read(fp, buffer, size); - if (ret < 0) - goto fail; - - if ((size_t)ret < size) { - fputs("Reading tar record: unexpected end-of-file.\n", stderr); - goto fail; - } - - if (skip_padding(fp, size)) - goto fail; - - buffer[size] = '\0'; - return buffer; -fail_errno: - perror("reading tar record"); - goto fail; -fail: - free(buffer); - return NULL; -} diff --git a/lib/tar/src/checksum.c b/lib/tar/src/checksum.c new file mode 100644 index 0000000..6541373 --- /dev/null +++ b/lib/tar/src/checksum.c @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * checksum.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "config.h" + +#include "tar/format.h" + +#include + +static unsigned int get_checksum(const tar_header_t *hdr) +{ + const unsigned char *header_start = (const unsigned char *)hdr; + const unsigned char *chksum_start = (const unsigned char *)hdr->chksum; + const unsigned char *header_end = header_start + sizeof(*hdr); + const unsigned char *chksum_end = chksum_start + sizeof(hdr->chksum); + const unsigned char *p; + unsigned int chksum = 0; + + for (p = header_start; p < chksum_start; p++) + chksum += *p; + for (; p < chksum_end; p++) + chksum += ' '; + for (; p < header_end; p++) + chksum += *p; + return chksum; +} + +void update_checksum(tar_header_t *hdr) +{ + unsigned int chksum = get_checksum(hdr); + + sprintf(hdr->chksum, "%06o", chksum); + hdr->chksum[6] = '\0'; + hdr->chksum[7] = ' '; +} + +bool is_checksum_valid(const tar_header_t *hdr) +{ + unsigned int calculated_chksum = get_checksum(hdr); + sqfs_u64 read_chksum; + + if (read_octal(hdr->chksum, sizeof(hdr->chksum), &read_chksum)) + return 0; + return read_chksum == calculated_chksum; +} diff --git a/lib/tar/src/cleanup.c b/lib/tar/src/cleanup.c new file mode 100644 index 0000000..9f33336 --- /dev/null +++ b/lib/tar/src/cleanup.c @@ -0,0 +1,42 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * cleanup.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "config.h" + +#include "internal.h" +#include +#include + +void free_sparse_list(sparse_map_t *sparse) +{ + sparse_map_t *old; + + while (sparse != NULL) { + old = sparse; + sparse = sparse->next; + free(old); + } +} + +void free_xattr_list(tar_xattr_t *list) +{ + tar_xattr_t *old; + + while (list != NULL) { + old = list; + list = list->next; + free(old); + } +} + +void clear_header(tar_header_decoded_t *hdr) +{ + free_xattr_list(hdr->xattr); + free_sparse_list(hdr->sparse); + free(hdr->name); + free(hdr->link_target); + memset(hdr, 0, sizeof(*hdr)); +} diff --git a/lib/tar/src/internal.h b/lib/tar/src/internal.h new file mode 100644 index 0000000..c5483fe --- /dev/null +++ b/lib/tar/src/internal.h @@ -0,0 +1,46 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * internal.h + * + * Copyright (C) 2019 David Oberhollenzer + */ +#ifndef INTERNAL_H +#define INTERNAL_H + +#include "config.h" + +#include "tar/tar.h" +#include "tar/format.h" +#include "util/util.h" + +enum { + PAX_SIZE = 0x001, + PAX_UID = 0x002, + PAX_GID = 0x004, + PAX_DEV_MAJ = 0x008, + PAX_DEV_MIN = 0x010, + PAX_NAME = 0x020, + PAX_SLINK_TARGET = 0x040, + PAX_MTIME = 0x100, + PAX_SPARSE_SIZE = 0x400, + + PAX_SPARSE_GNU_1_X = 0x800, +}; + +enum { + ETV_UNKNOWN = 0, + ETV_V7_UNIX, + ETV_PRE_POSIX, + ETV_POSIX, +}; + +sparse_map_t *read_gnu_old_sparse(istream_t *fp, tar_header_t *hdr); + +sparse_map_t *read_gnu_new_sparse(istream_t *fp, tar_header_decoded_t *out); + +char *record_to_memory(istream_t *fp, size_t size); + +int read_pax_header(istream_t *fp, sqfs_u64 entsize, unsigned int *set_by_pax, + tar_header_decoded_t *out); + +#endif /* INTERNAL_H */ diff --git a/lib/tar/src/number.c b/lib/tar/src/number.c new file mode 100644 index 0000000..2f179df --- /dev/null +++ b/lib/tar/src/number.c @@ -0,0 +1,79 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * number.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "config.h" + +#include "tar/format.h" + +#include +#include + +int read_octal(const char *str, int digits, sqfs_u64 *out) +{ + sqfs_u64 result = 0; + + while (digits > 0 && isspace(*str)) { + ++str; + --digits; + } + + while (digits > 0 && *str >= '0' && *str <= '7') { + if (result > 0x1FFFFFFFFFFFFFFFUL) { + fputs("numeric overflow parsing tar header\n", stderr); + return -1; + } + + result = (result << 3) | (*(str++) - '0'); + --digits; + } + + *out = result; + return 0; +} + +static int read_binary(const char *str, int digits, sqfs_u64 *out) +{ + sqfs_u64 x, ov, result = 0; + bool first = true; + + while (digits > 0) { + x = *((const unsigned char *)str++); + --digits; + + if (first) { + first = false; + if (x == 0xFF) { + result = 0xFFFFFFFFFFFFFFFFUL; + } else { + x &= 0x7F; + result = 0; + if (digits > 7 && x != 0) + goto fail_ov; + } + } + + ov = (result >> 56) & 0xFF; + + if (ov != 0 && ov != 0xFF) + goto fail_ov; + + result = (result << 8) | x; + } + + *out = result; + return 0; +fail_ov: + fputs("numeric overflow parsing tar header\n", stderr); + return -1; +} + +int read_number(const char *str, int digits, sqfs_u64 *out) +{ + if (*((const unsigned char *)str) & 0x80) + return read_binary(str, digits, out); + + return read_octal(str, digits, out); +} diff --git a/lib/tar/src/padd_file.c b/lib/tar/src/padd_file.c new file mode 100644 index 0000000..053ff1e --- /dev/null +++ b/lib/tar/src/padd_file.c @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * padd_file.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "config.h" +#include "tar/tar.h" +#include "tar/format.h" + +int padd_file(ostream_t *fp, sqfs_u64 size) +{ + size_t padd_sz = size % TAR_RECORD_SIZE; + + if (padd_sz == 0) + return 0; + + return ostream_append_sparse(fp, TAR_RECORD_SIZE - padd_sz); +} diff --git a/lib/tar/src/pax_header.c b/lib/tar/src/pax_header.c new file mode 100644 index 0000000..b61aab6 --- /dev/null +++ b/lib/tar/src/pax_header.c @@ -0,0 +1,402 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * pax_header.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "config.h" + +#include "internal.h" +#include +#include +#include + +static int pax_read_decimal(const char *str, sqfs_u64 *out) +{ + sqfs_u64 result = 0; + + while (*str >= '0' && *str <= '9') { + if (result > 0xFFFFFFFFFFFFFFFFUL / 10) { + fputs("numeric overflow parsing pax header\n", stderr); + return -1; + } + + result = (result * 10) + (*(str++) - '0'); + } + + *out = result; + return 0; +} + +static void urldecode(char *str) +{ + unsigned char *out = (unsigned char *)str; + char *in = str; + + while (*in != '\0') { + sqfs_u8 x = *(in++); + + if (x == '%' && isxdigit(in[0]) && isxdigit(in[1])) { + hex_decode(in, 2, &x, 1); + in += 2; + } + + *(out++) = x; + } + + *out = '\0'; +} + +static int pax_uid(tar_header_decoded_t *out, sqfs_u64 id) +{ + out->uid = id; + return 0; +} + +static int pax_gid(tar_header_decoded_t *out, sqfs_u64 id) +{ + out->gid = id; + return 0; +} + +static int pax_size(tar_header_decoded_t *out, sqfs_u64 size) +{ + out->record_size = size; + return 0; +} + +static int pax_mtime(tar_header_decoded_t *out, sqfs_s64 mtime) +{ + out->mtime = mtime; + return 0; +} + +static int pax_rsize(tar_header_decoded_t *out, sqfs_u64 size) +{ + out->actual_size = size; + return 0; +} + +static int pax_path(tar_header_decoded_t *out, char *path) +{ + free(out->name); + out->name = path; + return 0; +} + +static int pax_slink(tar_header_decoded_t *out, char *path) +{ + free(out->link_target); + out->link_target = path; + return 0; +} + +static int pax_sparse_map(tar_header_decoded_t *out, const char *line) +{ + sparse_map_t *last = NULL, *list = NULL, *ent = NULL; + + free_sparse_list(out->sparse); + out->sparse = NULL; + + do { + ent = calloc(1, sizeof(*ent)); + if (ent == NULL) + goto fail_errno; + + if (pax_read_decimal(line, &ent->offset)) + goto fail_format; + + while (isdigit(*line)) + ++line; + + if (*(line++) != ',') + goto fail_format; + + if (pax_read_decimal(line, &ent->count)) + goto fail_format; + + while (isdigit(*line)) + ++line; + + if (last == NULL) { + list = last = ent; + } else { + last->next = ent; + last = ent; + } + } while (*(line++) == ','); + + out->sparse = list; + return 0; +fail_errno: + perror("parsing GNU pax sparse file record"); + goto fail; +fail_format: + fputs("malformed GNU pax sparse file record\n", stderr); + goto fail; +fail: + free_sparse_list(list); + free(ent); + return -1; +} + +static int pax_xattr_schily(tar_header_decoded_t *out, + tar_xattr_t *xattr) +{ + xattr->next = out->xattr; + out->xattr = xattr; + return 0; +} + +static int pax_xattr_libarchive(tar_header_decoded_t *out, + tar_xattr_t *xattr) +{ + int ret; + + ret = base64_decode((const char *)xattr->value, xattr->value_len, + xattr->value, &xattr->value_len); + if (ret) + return -1; + + urldecode(xattr->key); + + xattr->value[xattr->value_len] = '\0'; + xattr->next = out->xattr; + out->xattr = xattr; + return 0; +} + +enum { + PAX_TYPE_SINT = 0, + PAX_TYPE_UINT, + PAX_TYPE_STRING, + PAX_TYPE_CONST_STRING, + PAX_TYPE_PREFIXED_XATTR, + PAX_TYPE_IGNORE, +}; + +static const struct pax_handler_t { + const char *name; + int flag; + int type; + union { + int (*sint)(tar_header_decoded_t *out, sqfs_s64 sval); + int (*uint)(tar_header_decoded_t *out, sqfs_u64 uval); + int (*str)(tar_header_decoded_t *out, char *str); + int (*cstr)(tar_header_decoded_t *out, const char *str); + int (*xattr)(tar_header_decoded_t *out, tar_xattr_t *xattr); + } cb; +} pax_fields[] = { + { "uid", PAX_UID, PAX_TYPE_UINT, { .uint = pax_uid } }, + { "gid", PAX_GID, PAX_TYPE_UINT, { .uint = pax_gid } }, + { "path", PAX_NAME, PAX_TYPE_STRING, { .str = pax_path } }, + { "size", PAX_SIZE, PAX_TYPE_UINT, { .uint = pax_size } }, + { "linkpath", PAX_SLINK_TARGET, PAX_TYPE_STRING, { .str = pax_slink } }, + { "mtime", PAX_MTIME, PAX_TYPE_SINT, { .sint = pax_mtime } }, + { "GNU.sparse.name", PAX_NAME, PAX_TYPE_STRING, { .str = pax_path } }, + { "GNU.sparse.size", PAX_SPARSE_SIZE, PAX_TYPE_UINT, + {.uint = pax_rsize} }, + { "GNU.sparse.realsize", PAX_SPARSE_SIZE, PAX_TYPE_UINT, + {.uint = pax_rsize} }, + { "GNU.sparse.major", PAX_SPARSE_GNU_1_X, PAX_TYPE_IGNORE, + { .str = NULL } }, + { "GNU.sparse.minor", PAX_SPARSE_GNU_1_X, PAX_TYPE_IGNORE, + { .str = NULL }}, + { "SCHILY.xattr", 0, PAX_TYPE_PREFIXED_XATTR, + { .xattr = pax_xattr_schily } }, + { "LIBARCHIVE.xattr", 0, PAX_TYPE_PREFIXED_XATTR, + { .xattr = pax_xattr_libarchive } }, + { "GNU.sparse.map", 0, PAX_TYPE_CONST_STRING, + { .cstr = pax_sparse_map } }, +}; + +static const struct pax_handler_t *find_handler(const char *key) +{ + size_t i, fieldlen; + + for (i = 0; i < sizeof(pax_fields) / sizeof(pax_fields[0]); ++i) { + if (pax_fields[i].type == PAX_TYPE_PREFIXED_XATTR) { + fieldlen = strlen(pax_fields[i].name); + + if (strncmp(key, pax_fields[i].name, fieldlen)) + continue; + + if (key[fieldlen] != '.') + continue; + + return pax_fields + i; + } + + if (!strcmp(key, pax_fields[i].name)) + return pax_fields + i; + } + + return NULL; +} + +static tar_xattr_t *mkxattr(const char *key, + const char *value, size_t valuelen) +{ + size_t keylen = strlen(key); + tar_xattr_t *xattr; + + xattr = calloc(1, sizeof(*xattr) + keylen + 1 + valuelen + 1); + if (xattr == NULL) + return NULL; + + xattr->key = xattr->data; + memcpy(xattr->key, key, keylen); + xattr->key[keylen] = '\0'; + + xattr->value = (sqfs_u8 *)xattr->key + keylen + 1; + memcpy(xattr->value, value, valuelen); + xattr->value[valuelen] = '\0'; + + xattr->value_len = valuelen; + return xattr; +} + +static int apply_handler(tar_header_decoded_t *out, + const struct pax_handler_t *field, const char *key, + const char *value, size_t valuelen) +{ + tar_xattr_t *xattr; + sqfs_s64 s64val; + sqfs_u64 uval; + char *copy; + + switch (field->type) { + case PAX_TYPE_SINT: + if (value[0] == '-') { + if (pax_read_decimal(value + 1, &uval)) + return -1; + s64val = -((sqfs_s64)uval); + } else { + if (pax_read_decimal(value, &uval)) + return -1; + s64val = (sqfs_s64)uval; + } + return field->cb.sint(out, s64val); + case PAX_TYPE_UINT: + if (pax_read_decimal(value, &uval)) + return -1; + return field->cb.uint(out, uval); + case PAX_TYPE_CONST_STRING: + return field->cb.cstr(out, value); + case PAX_TYPE_STRING: + copy = strdup(value); + if (copy == NULL) { + perror("processing pax header"); + return -1; + } + if (field->cb.str(out, copy)) { + free(copy); + return -1; + } + break; + case PAX_TYPE_PREFIXED_XATTR: + xattr = mkxattr(key + strlen(field->name) + 1, + value, valuelen); + if (xattr == NULL) { + perror("reading pax xattr field"); + return -1; + } + if (field->cb.xattr(out, xattr)) { + free(xattr); + return -1; + } + break; + default: + break; + } + + return 0; +} + +int read_pax_header(istream_t *fp, sqfs_u64 entsize, unsigned int *set_by_pax, + tar_header_decoded_t *out) +{ + char *buffer, *line, *key, *ptr, *value, *end; + sparse_map_t *sparse_last = NULL, *sparse; + sqfs_u64 offset = 0, num_bytes = 0; + const struct pax_handler_t *field; + long len; + + buffer = record_to_memory(fp, entsize); + if (buffer == NULL) + return -1; + + end = buffer + entsize; + + for (line = buffer; line < end; line += len) { + len = strtol(line, &ptr, 10); + if (ptr == line || !isspace(*ptr) || len <= 0) + goto fail_malformed; + + if (len > (end - line)) + goto fail_ov; + + line[len - 1] = '\0'; + + while (ptr < end && isspace(*ptr)) + ++ptr; + + if (ptr >= end || (ptr - line) >= len) + goto fail_malformed; + + key = ptr; + + while (*ptr != '\0' && *ptr != '=') + ++ptr; + + if (ptr == key || *ptr != '=') + goto fail_malformed; + + *(ptr++) = '\0'; + value = ptr; + + field = find_handler(key); + + if (field != NULL) { + if (apply_handler(out, field, key, value, + len - (value - line) - 1)) { + goto fail; + } + + *set_by_pax |= field->flag; + } else if (!strcmp(key, "GNU.sparse.offset")) { + if (pax_read_decimal(value, &offset)) + goto fail; + } else if (!strcmp(key, "GNU.sparse.numbytes")) { + if (pax_read_decimal(value, &num_bytes)) + goto fail; + sparse = calloc(1, sizeof(*sparse)); + if (sparse == NULL) + goto fail_errno; + sparse->offset = offset; + sparse->count = num_bytes; + if (sparse_last == NULL) { + free_sparse_list(out->sparse); + out->sparse = sparse_last = sparse; + } else { + sparse_last->next = sparse; + sparse_last = sparse; + } + } + } + + free(buffer); + return 0; +fail_malformed: + fputs("Found a malformed PAX header.\n", stderr); + goto fail; +fail_ov: + fputs("Numeric overflow in PAX header.\n", stderr); + goto fail; +fail_errno: + perror("reading pax header"); + goto fail; +fail: + free(buffer); + return -1; +} diff --git a/lib/tar/src/read_header.c b/lib/tar/src/read_header.c new file mode 100644 index 0000000..ea4873b --- /dev/null +++ b/lib/tar/src/read_header.c @@ -0,0 +1,304 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * read_header.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "config.h" + +#include "internal.h" +#include +#include + +static bool is_zero_block(const tar_header_t *hdr) +{ + const unsigned char *ptr = (const unsigned char *)hdr; + + return ptr[0] == '\0' && memcmp(ptr, ptr + 1, sizeof(*hdr) - 1) == 0; +} + +static int check_version(const tar_header_t *hdr) +{ + char buffer[sizeof(hdr->magic) + sizeof(hdr->version)]; + + memset(buffer, '\0', sizeof(buffer)); + if (memcmp(hdr->magic, buffer, sizeof(hdr->magic)) == 0 && + memcmp(hdr->version, buffer, sizeof(hdr->version)) == 0) + return ETV_V7_UNIX; + + if (memcmp(hdr->magic, TAR_MAGIC, sizeof(hdr->magic)) == 0 && + memcmp(hdr->version, TAR_VERSION, sizeof(hdr->version)) == 0) + return ETV_POSIX; + + if (memcmp(hdr->magic, TAR_MAGIC_OLD, sizeof(hdr->magic)) == 0 && + memcmp(hdr->version, TAR_VERSION_OLD, sizeof(hdr->version)) == 0) + return ETV_PRE_POSIX; + + return ETV_UNKNOWN; +} + +static int decode_header(const tar_header_t *hdr, unsigned int set_by_pax, + tar_header_decoded_t *out, int version) +{ + size_t len1, len2; + sqfs_u64 field; + + if (!(set_by_pax & PAX_NAME)) { + if (hdr->tail.posix.prefix[0] != '\0' && + version == ETV_POSIX) { + len1 = strnlen(hdr->name, sizeof(hdr->name)); + len2 = strnlen(hdr->tail.posix.prefix, + sizeof(hdr->tail.posix.prefix)); + + out->name = malloc(len1 + 1 + len2 + 1); + + if (out->name != NULL) { + memcpy(out->name, hdr->tail.posix.prefix, len2); + out->name[len2] = '/'; + memcpy(out->name + len2 + 1, hdr->name, len1); + out->name[len1 + 1 + len2] = '\0'; + } + } else { + out->name = strndup(hdr->name, sizeof(hdr->name)); + } + + if (out->name == NULL) { + perror("decoding filename"); + return -1; + } + } + + if (!(set_by_pax & PAX_SIZE)) { + if (read_number(hdr->size, sizeof(hdr->size), &out->record_size)) + return -1; + } + + if (!(set_by_pax & PAX_UID)) { + if (read_number(hdr->uid, sizeof(hdr->uid), &field)) + return -1; + out->uid = field; + } + + if (!(set_by_pax & PAX_GID)) { + if (read_number(hdr->gid, sizeof(hdr->gid), &field)) + return -1; + out->gid = field; + } + + if (!(set_by_pax & PAX_DEV_MAJ)) { + if (read_number(hdr->devmajor, sizeof(hdr->devmajor), &field)) + return -1; + + out->devno = makedev(field, minor(out->devno)); + } + + if (!(set_by_pax & PAX_DEV_MIN)) { + if (read_number(hdr->devminor, sizeof(hdr->devminor), &field)) + return -1; + + out->devno = makedev(major(out->devno), field); + } + + if (!(set_by_pax & PAX_MTIME)) { + if (read_number(hdr->mtime, sizeof(hdr->mtime), &field)) + return -1; + if (field & 0x8000000000000000UL) { + field = ~field + 1; + out->mtime = -((sqfs_s64)field); + } else { + out->mtime = field; + } + } + + if (read_octal(hdr->mode, sizeof(hdr->mode), &field)) + return -1; + + out->mode = field & 07777; + + if (hdr->typeflag == TAR_TYPE_LINK || + hdr->typeflag == TAR_TYPE_SLINK) { + if (!(set_by_pax & PAX_SLINK_TARGET)) { + out->link_target = strndup(hdr->linkname, + sizeof(hdr->linkname)); + if (out->link_target == NULL) { + perror("decoding symlink target"); + return -1; + } + } + } + + out->unknown_record = false; + + switch (hdr->typeflag) { + case '\0': + case TAR_TYPE_FILE: + case TAR_TYPE_GNU_SPARSE: + out->mode |= S_IFREG; + break; + case TAR_TYPE_LINK: + out->is_hard_link = true; + break; + case TAR_TYPE_SLINK: + out->mode = S_IFLNK | 0777; + break; + case TAR_TYPE_CHARDEV: + out->mode |= S_IFCHR; + break; + case TAR_TYPE_BLOCKDEV: + out->mode |= S_IFBLK; + break; + case TAR_TYPE_DIR: + out->mode |= S_IFDIR; + break; + case TAR_TYPE_FIFO: + out->mode |= S_IFIFO; + break; + default: + out->unknown_record = true; + break; + } + + return 0; +} + +int read_header(istream_t *fp, tar_header_decoded_t *out) +{ + unsigned int set_by_pax = 0; + bool prev_was_zero = false; + sqfs_u64 pax_size; + tar_header_t hdr; + int version, ret; + + memset(out, 0, sizeof(*out)); + + for (;;) { + ret = istream_read(fp, &hdr, sizeof(hdr)); + if (ret < 0) + goto fail; + + if ((size_t)ret < sizeof(hdr)) + goto out_eof; + + if (is_zero_block(&hdr)) { + if (prev_was_zero) + goto out_eof; + prev_was_zero = true; + continue; + } + + prev_was_zero = false; + version = check_version(&hdr); + + if (version == ETV_UNKNOWN) + goto fail_magic; + + if (!is_checksum_valid(&hdr)) + goto fail_chksum; + + switch (hdr.typeflag) { + case TAR_TYPE_GNU_SLINK: + if (read_number(hdr.size, sizeof(hdr.size), &pax_size)) + goto fail; + if (pax_size < 1 || pax_size > TAR_MAX_SYMLINK_LEN) + goto fail_slink_len; + free(out->link_target); + out->link_target = record_to_memory(fp, pax_size); + if (out->link_target == NULL) + goto fail; + set_by_pax |= PAX_SLINK_TARGET; + continue; + case TAR_TYPE_GNU_PATH: + if (read_number(hdr.size, sizeof(hdr.size), &pax_size)) + goto fail; + if (pax_size < 1 || pax_size > TAR_MAX_PATH_LEN) + goto fail_path_len; + free(out->name); + out->name = record_to_memory(fp, pax_size); + if (out->name == NULL) + goto fail; + set_by_pax |= PAX_NAME; + continue; + case TAR_TYPE_PAX_GLOBAL: + if (read_number(hdr.size, sizeof(hdr.size), &pax_size)) + goto fail; + skip_entry(fp, pax_size); + continue; + case TAR_TYPE_PAX: + clear_header(out); + if (read_number(hdr.size, sizeof(hdr.size), &pax_size)) + goto fail; + if (pax_size < 1 || pax_size > TAR_MAX_PAX_LEN) + goto fail_pax_len; + set_by_pax = 0; + if (read_pax_header(fp, pax_size, &set_by_pax, out)) + goto fail; + continue; + case TAR_TYPE_GNU_SPARSE: + free_sparse_list(out->sparse); + out->sparse = read_gnu_old_sparse(fp, &hdr); + if (out->sparse == NULL) + goto fail; + if (read_number(hdr.tail.gnu.realsize, + sizeof(hdr.tail.gnu.realsize), + &out->actual_size)) + goto fail; + break; + default: + break; + } + break; + } + + if (decode_header(&hdr, set_by_pax, out, version)) + goto fail; + + if (set_by_pax & PAX_SPARSE_GNU_1_X) { + free_sparse_list(out->sparse); + out->sparse = read_gnu_new_sparse(fp, out); + if (out->sparse == NULL) + goto fail; + } + + if (out->sparse == NULL) + out->actual_size = out->record_size; + + return 0; +out_eof: + clear_header(out); + return 1; +fail_slink_len: + fprintf(stderr, "rejecting GNU symlink header with size %lu\n", + (unsigned long)pax_size); + goto fail; +fail_path_len: + fprintf(stderr, "rejecting GNU long path header with size %lu\n", + (unsigned long)pax_size); + goto fail; +fail_pax_len: + fprintf(stderr, "rejecting PAX header with size %lu\n", + (unsigned long)pax_size); + goto fail; +fail_magic: + fputs("input is not a ustar tar archive!\n", stderr); + goto fail; +fail_chksum: + fputs("invalid tar header checksum!\n", stderr); + goto fail; +fail: + clear_header(out); + return -1; +} + +int skip_padding(istream_t *fp, sqfs_u64 size) +{ + size_t tail = size % 512; + + return tail ? istream_skip(fp, 512 - tail) : 0; +} + +int skip_entry(istream_t *fp, sqfs_u64 size) +{ + size_t tail = size % 512; + + return istream_skip(fp, tail ? (size + 512 - tail) : size); +} diff --git a/lib/tar/src/read_sparse_map_new.c b/lib/tar/src/read_sparse_map_new.c new file mode 100644 index 0000000..de1b6a4 --- /dev/null +++ b/lib/tar/src/read_sparse_map_new.c @@ -0,0 +1,115 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * read_sparse_map_new.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "config.h" +#include "internal.h" + +#include +#include +#include + +static int decode(const char *str, size_t len, size_t *out) +{ + size_t count = 0; + + *out = 0; + + while (count < len && isdigit(*str)) { + if (SZ_MUL_OV(*out, 10, out)) + return -1; + if (SZ_ADD_OV(*out, (*(str++) - '0'), out)) + return -1; + ++count; + } + + if (count == 0 || count == len) + return 0; + + return (*str == '\n') ? ((int)count + 1) : -1; +} + +sparse_map_t *read_gnu_new_sparse(istream_t *fp, tar_header_decoded_t *out) +{ + sparse_map_t *last = NULL, *list = NULL, *ent = NULL; + size_t i, count, value; + char buffer[1024]; + int diff, ret; + + if (out->record_size < 512) + goto fail_format; + + ret = istream_read(fp, buffer, 512); + if (ret < 0) + goto fail; + + if (ret < 512) + goto fail_format; + + diff = decode(buffer, 512, &count); + if (diff <= 0) + goto fail_format; + + out->record_size -= 512; + + if (count == 0 || count > TAR_MAX_SPARSE_ENT) + goto fail_format; + + for (i = 0; i < (count * 2); ++i) { + ret = decode(buffer + diff, 512 - diff, &value); + if (ret < 0) + goto fail_format; + + if (ret > 0) { + diff += ret; + } else { + if (out->record_size < 512) + goto fail_format; + + ret = istream_read(fp, buffer + 512, 512); + if (ret < 0) + goto fail; + + if (ret < 512) + goto fail_format; + + ret = decode(buffer + diff, 1024 - diff, &value); + if (ret <= 0) + goto fail_format; + + memcpy(buffer, buffer + 512, 512); + diff = diff + ret - 512; + out->record_size -= 512; + } + + if ((i & 0x01) == 0) { + ent = calloc(1, sizeof(*ent)); + if (ent == NULL) + goto fail_errno; + + if (list == NULL) { + list = last = ent; + } else { + last->next = ent; + last = ent; + } + + ent->offset = value; + } else { + ent->count = value; + } + } + + return list; +fail_errno: + perror("parsing GNU 1.0 style sparse file record"); + goto fail; +fail_format: + fputs("Malformed GNU 1.0 style sparse file map.\n", stderr); + goto fail; +fail: + free_sparse_list(list); + return NULL; +} diff --git a/lib/tar/src/read_sparse_map_old.c b/lib/tar/src/read_sparse_map_old.c new file mode 100644 index 0000000..3dd3300 --- /dev/null +++ b/lib/tar/src/read_sparse_map_old.c @@ -0,0 +1,99 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * read_sparse_map_old.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "config.h" +#include "internal.h" + +#include +#include + +sparse_map_t *read_gnu_old_sparse(istream_t *fp, tar_header_t *hdr) +{ + sparse_map_t *list = NULL, *end = NULL, *node; + gnu_sparse_t sph; + sqfs_u64 off, sz; + int i, ret; + + for (i = 0; i < 4; ++i) { + if (!isdigit(hdr->tail.gnu.sparse[i].offset[0])) + break; + if (!isdigit(hdr->tail.gnu.sparse[i].numbytes[0])) + break; + + if (read_octal(hdr->tail.gnu.sparse[i].offset, + sizeof(hdr->tail.gnu.sparse[i].offset), &off)) + goto fail; + if (read_octal(hdr->tail.gnu.sparse[i].numbytes, + sizeof(hdr->tail.gnu.sparse[i].numbytes), &sz)) + goto fail; + + node = calloc(1, sizeof(*node)); + if (node == NULL) + goto fail_errno; + + node->offset = off; + node->count = sz; + + if (list == NULL) { + list = end = node; + } else { + end->next = node; + end = node; + } + } + + if (hdr->tail.gnu.isextended == 0) + return list; + + do { + ret = istream_read(fp, &sph, sizeof(sph)); + if (ret < 0) + goto fail; + + if ((size_t)ret < sizeof(sph)) { + fputs("reading GNU sparse header: " + "unexpected end-of-file\n", + stderr); + goto fail; + } + + for (i = 0; i < 21; ++i) { + if (!isdigit(sph.sparse[i].offset[0])) + break; + if (!isdigit(sph.sparse[i].numbytes[0])) + break; + + if (read_octal(sph.sparse[i].offset, + sizeof(sph.sparse[i].offset), &off)) + goto fail; + if (read_octal(sph.sparse[i].numbytes, + sizeof(sph.sparse[i].numbytes), &sz)) + goto fail; + + node = calloc(1, sizeof(*node)); + if (node == NULL) + goto fail_errno; + + node->offset = off; + node->count = sz; + + if (list == NULL) { + list = end = node; + } else { + end->next = node; + end = node; + } + } + } while (sph.isextended != 0); + + return list; +fail_errno: + perror("parsing GNU sparse header"); + goto fail; +fail: + free_sparse_list(list); + return NULL; +} diff --git a/lib/tar/src/record_to_memory.c b/lib/tar/src/record_to_memory.c new file mode 100644 index 0000000..ba422de --- /dev/null +++ b/lib/tar/src/record_to_memory.c @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * record_to_memory.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "config.h" + +#include "tar/tar.h" +#include "internal.h" +#include + +char *record_to_memory(istream_t *fp, size_t size) +{ + char *buffer = malloc(size + 1); + int ret; + + if (buffer == NULL) + goto fail_errno; + + ret = istream_read(fp, buffer, size); + if (ret < 0) + goto fail; + + if ((size_t)ret < size) { + fputs("Reading tar record: unexpected end-of-file.\n", stderr); + goto fail; + } + + if (skip_padding(fp, size)) + goto fail; + + buffer[size] = '\0'; + return buffer; +fail_errno: + perror("reading tar record"); + goto fail; +fail: + free(buffer); + return NULL; +} diff --git a/lib/tar/src/write_header.c b/lib/tar/src/write_header.c new file mode 100644 index 0000000..b0711b3 --- /dev/null +++ b/lib/tar/src/write_header.c @@ -0,0 +1,282 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * write_header.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "config.h" + +#include "internal.h" +#include + +static void write_binary(char *dst, sqfs_u64 value, int digits) +{ + memset(dst, 0, digits); + + while (digits > 0) { + ((unsigned char *)dst)[digits - 1] = value & 0xFF; + --digits; + value >>= 8; + } + + ((unsigned char *)dst)[0] |= 0x80; +} + +static void write_number(char *dst, sqfs_u64 value, int digits) +{ + sqfs_u64 mask = 0; + char buffer[64]; + int i; + + for (i = 0; i < (digits - 1); ++i) + mask = (mask << 3) | 7; + + if (value <= mask) { + sprintf(buffer, "%0*lo ", digits - 1, (unsigned long)value); + memcpy(dst, buffer, digits); + } else if (value <= ((mask << 3) | 7)) { + sprintf(buffer, "%0*lo", digits, (unsigned long)value); + memcpy(dst, buffer, digits); + } else { + write_binary(dst, value, digits); + } +} + +static void write_number_signed(char *dst, sqfs_s64 value, int digits) +{ + sqfs_u64 neg; + + if (value < 0) { + neg = -value; + write_binary(dst, ~neg + 1, digits); + } else { + write_number(dst, value, digits); + } +} + +static int write_header(ostream_t *fp, const struct stat *sb, const char *name, + const char *slink_target, int type) +{ + int maj = 0, min = 0; + sqfs_u64 size = 0; + tar_header_t hdr; + + if (S_ISCHR(sb->st_mode) || S_ISBLK(sb->st_mode)) { + maj = major(sb->st_rdev); + min = minor(sb->st_rdev); + } + + if (S_ISREG(sb->st_mode)) + size = sb->st_size; + + memset(&hdr, 0, sizeof(hdr)); + + strncpy(hdr.name, name, sizeof(hdr.name) - 1); + write_number(hdr.mode, sb->st_mode & ~S_IFMT, sizeof(hdr.mode)); + write_number(hdr.uid, sb->st_uid, sizeof(hdr.uid)); + write_number(hdr.gid, sb->st_gid, sizeof(hdr.gid)); + write_number(hdr.size, size, sizeof(hdr.size)); + write_number_signed(hdr.mtime, sb->st_mtime, sizeof(hdr.mtime)); + hdr.typeflag = type; + if (slink_target != NULL) + memcpy(hdr.linkname, slink_target, sb->st_size); + memcpy(hdr.magic, TAR_MAGIC_OLD, sizeof(hdr.magic)); + memcpy(hdr.version, TAR_VERSION_OLD, sizeof(hdr.version)); + sprintf(hdr.uname, "%u", sb->st_uid); + sprintf(hdr.gname, "%u", sb->st_gid); + write_number(hdr.devmajor, maj, sizeof(hdr.devmajor)); + write_number(hdr.devminor, min, sizeof(hdr.devminor)); + + update_checksum(&hdr); + + return ostream_append(fp, &hdr, sizeof(hdr)); +} + +static int write_gnu_header(ostream_t *fp, const struct stat *orig, + const char *payload, size_t payload_len, + int type, const char *name) +{ + struct stat sb; + + sb = *orig; + sb.st_mode = S_IFREG | 0644; + sb.st_size = payload_len; + + if (write_header(fp, &sb, name, NULL, type)) + return -1; + + if (ostream_append(fp, payload, payload_len)) + return -1; + + return padd_file(fp, payload_len); +} + +static size_t num_digits(size_t num) +{ + size_t i = 1; + + while (num >= 10) { + num /= 10; + ++i; + } + + return i; +} + +static size_t prefix_digit_len(size_t len) +{ + size_t old_ndigit, ndigit = 0; + + do { + old_ndigit = ndigit; + ndigit = num_digits(len + ndigit); + } while (old_ndigit != ndigit); + + return ndigit; +} + +static int write_schily_xattr(ostream_t *fp, const struct stat *orig, + const char *name, const tar_xattr_t *xattr) +{ + static const char *prefix = "SCHILY.xattr."; + size_t len, total_size = 0; + const tar_xattr_t *it; + struct stat sb; + + for (it = xattr; it != NULL; it = it->next) { + len = strlen(prefix) + strlen(it->key) + it->value_len + 3; + + total_size += len + prefix_digit_len(len); + } + + sb = *orig; + sb.st_mode = S_IFREG | 0644; + sb.st_size = total_size; + + if (write_header(fp, &sb, name, NULL, TAR_TYPE_PAX)) + return -1; + + for (it = xattr; it != NULL; it = it->next) { + len = strlen(prefix) + strlen(it->key) + it->value_len + 3; + len += prefix_digit_len(len); + + if (ostream_printf(fp, PRI_SZ " %s%s=", + len, prefix, it->key) < 0) { + return -1; + } + if (ostream_append(fp, it->value, it->value_len)) + return -1; + if (ostream_append(fp, "\n", 1)) + return -1; + } + + return padd_file(fp, total_size); +} + +int write_tar_header(ostream_t *fp, const struct stat *sb, const char *name, + const char *slink_target, const tar_xattr_t *xattr, + unsigned int counter) +{ + const char *reason; + char buffer[64]; + int type; + + if (xattr != NULL) { + sprintf(buffer, "pax/xattr%u", counter); + + if (write_schily_xattr(fp, sb, buffer, xattr)) + return -1; + } + + if (!S_ISLNK(sb->st_mode)) + slink_target = NULL; + + if (S_ISLNK(sb->st_mode) && sb->st_size >= 100) { + sprintf(buffer, "gnu/target%u", counter); + if (write_gnu_header(fp, sb, slink_target, sb->st_size, + TAR_TYPE_GNU_SLINK, buffer)) + return -1; + slink_target = NULL; + } + + if (strlen(name) >= 100) { + sprintf(buffer, "gnu/name%u", counter); + + if (write_gnu_header(fp, sb, name, strlen(name), + TAR_TYPE_GNU_PATH, buffer)) { + return -1; + } + + sprintf(buffer, "gnu/data%u", counter); + name = buffer; + } + + switch (sb->st_mode & S_IFMT) { + case S_IFCHR: type = TAR_TYPE_CHARDEV; break; + case S_IFBLK: type = TAR_TYPE_BLOCKDEV; break; + case S_IFLNK: type = TAR_TYPE_SLINK; break; + case S_IFREG: type = TAR_TYPE_FILE; break; + case S_IFDIR: type = TAR_TYPE_DIR; break; + case S_IFIFO: type = TAR_TYPE_FIFO; break; + case S_IFSOCK: + reason = "cannot pack socket"; + goto out_skip; + default: + reason = "unknown type"; + goto out_skip; + } + + return write_header(fp, sb, name, slink_target, type); +out_skip: + fprintf(stderr, "WARNING: %s: %s\n", name, reason); + return 1; +} + +int write_hard_link(ostream_t *fp, const struct stat *sb, const char *name, + const char *target, unsigned int counter) +{ + tar_header_t hdr; + char buffer[64]; + size_t len; + + memset(&hdr, 0, sizeof(hdr)); + + len = strlen(target); + if (len >= 100) { + sprintf(buffer, "gnu/target%u", counter); + if (write_gnu_header(fp, sb, target, len, + TAR_TYPE_GNU_SLINK, buffer)) + return -1; + sprintf(hdr.linkname, "hardlink_%u", counter); + } else { + memcpy(hdr.linkname, target, len); + } + + len = strlen(name); + if (len >= 100) { + sprintf(buffer, "gnu/name%u", counter); + if (write_gnu_header(fp, sb, name, len, + TAR_TYPE_GNU_PATH, buffer)) { + return -1; + } + sprintf(hdr.name, "gnu/data%u", counter); + } else { + memcpy(hdr.name, name, len); + } + + write_number(hdr.mode, sb->st_mode & ~S_IFMT, sizeof(hdr.mode)); + write_number(hdr.uid, sb->st_uid, sizeof(hdr.uid)); + write_number(hdr.gid, sb->st_gid, sizeof(hdr.gid)); + write_number(hdr.size, 0, sizeof(hdr.size)); + write_number_signed(hdr.mtime, sb->st_mtime, sizeof(hdr.mtime)); + hdr.typeflag = TAR_TYPE_LINK; + memcpy(hdr.magic, TAR_MAGIC_OLD, sizeof(hdr.magic)); + memcpy(hdr.version, TAR_VERSION_OLD, sizeof(hdr.version)); + sprintf(hdr.uname, "%u", sb->st_uid); + sprintf(hdr.gname, "%u", sb->st_gid); + write_number(hdr.devmajor, 0, sizeof(hdr.devmajor)); + write_number(hdr.devminor, 0, sizeof(hdr.devminor)); + + update_checksum(&hdr); + return ostream_append(fp, &hdr, sizeof(hdr)); +} diff --git a/lib/tar/write_header.c b/lib/tar/write_header.c deleted file mode 100644 index b0711b3..0000000 --- a/lib/tar/write_header.c +++ /dev/null @@ -1,282 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * write_header.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "config.h" - -#include "internal.h" -#include - -static void write_binary(char *dst, sqfs_u64 value, int digits) -{ - memset(dst, 0, digits); - - while (digits > 0) { - ((unsigned char *)dst)[digits - 1] = value & 0xFF; - --digits; - value >>= 8; - } - - ((unsigned char *)dst)[0] |= 0x80; -} - -static void write_number(char *dst, sqfs_u64 value, int digits) -{ - sqfs_u64 mask = 0; - char buffer[64]; - int i; - - for (i = 0; i < (digits - 1); ++i) - mask = (mask << 3) | 7; - - if (value <= mask) { - sprintf(buffer, "%0*lo ", digits - 1, (unsigned long)value); - memcpy(dst, buffer, digits); - } else if (value <= ((mask << 3) | 7)) { - sprintf(buffer, "%0*lo", digits, (unsigned long)value); - memcpy(dst, buffer, digits); - } else { - write_binary(dst, value, digits); - } -} - -static void write_number_signed(char *dst, sqfs_s64 value, int digits) -{ - sqfs_u64 neg; - - if (value < 0) { - neg = -value; - write_binary(dst, ~neg + 1, digits); - } else { - write_number(dst, value, digits); - } -} - -static int write_header(ostream_t *fp, const struct stat *sb, const char *name, - const char *slink_target, int type) -{ - int maj = 0, min = 0; - sqfs_u64 size = 0; - tar_header_t hdr; - - if (S_ISCHR(sb->st_mode) || S_ISBLK(sb->st_mode)) { - maj = major(sb->st_rdev); - min = minor(sb->st_rdev); - } - - if (S_ISREG(sb->st_mode)) - size = sb->st_size; - - memset(&hdr, 0, sizeof(hdr)); - - strncpy(hdr.name, name, sizeof(hdr.name) - 1); - write_number(hdr.mode, sb->st_mode & ~S_IFMT, sizeof(hdr.mode)); - write_number(hdr.uid, sb->st_uid, sizeof(hdr.uid)); - write_number(hdr.gid, sb->st_gid, sizeof(hdr.gid)); - write_number(hdr.size, size, sizeof(hdr.size)); - write_number_signed(hdr.mtime, sb->st_mtime, sizeof(hdr.mtime)); - hdr.typeflag = type; - if (slink_target != NULL) - memcpy(hdr.linkname, slink_target, sb->st_size); - memcpy(hdr.magic, TAR_MAGIC_OLD, sizeof(hdr.magic)); - memcpy(hdr.version, TAR_VERSION_OLD, sizeof(hdr.version)); - sprintf(hdr.uname, "%u", sb->st_uid); - sprintf(hdr.gname, "%u", sb->st_gid); - write_number(hdr.devmajor, maj, sizeof(hdr.devmajor)); - write_number(hdr.devminor, min, sizeof(hdr.devminor)); - - update_checksum(&hdr); - - return ostream_append(fp, &hdr, sizeof(hdr)); -} - -static int write_gnu_header(ostream_t *fp, const struct stat *orig, - const char *payload, size_t payload_len, - int type, const char *name) -{ - struct stat sb; - - sb = *orig; - sb.st_mode = S_IFREG | 0644; - sb.st_size = payload_len; - - if (write_header(fp, &sb, name, NULL, type)) - return -1; - - if (ostream_append(fp, payload, payload_len)) - return -1; - - return padd_file(fp, payload_len); -} - -static size_t num_digits(size_t num) -{ - size_t i = 1; - - while (num >= 10) { - num /= 10; - ++i; - } - - return i; -} - -static size_t prefix_digit_len(size_t len) -{ - size_t old_ndigit, ndigit = 0; - - do { - old_ndigit = ndigit; - ndigit = num_digits(len + ndigit); - } while (old_ndigit != ndigit); - - return ndigit; -} - -static int write_schily_xattr(ostream_t *fp, const struct stat *orig, - const char *name, const tar_xattr_t *xattr) -{ - static const char *prefix = "SCHILY.xattr."; - size_t len, total_size = 0; - const tar_xattr_t *it; - struct stat sb; - - for (it = xattr; it != NULL; it = it->next) { - len = strlen(prefix) + strlen(it->key) + it->value_len + 3; - - total_size += len + prefix_digit_len(len); - } - - sb = *orig; - sb.st_mode = S_IFREG | 0644; - sb.st_size = total_size; - - if (write_header(fp, &sb, name, NULL, TAR_TYPE_PAX)) - return -1; - - for (it = xattr; it != NULL; it = it->next) { - len = strlen(prefix) + strlen(it->key) + it->value_len + 3; - len += prefix_digit_len(len); - - if (ostream_printf(fp, PRI_SZ " %s%s=", - len, prefix, it->key) < 0) { - return -1; - } - if (ostream_append(fp, it->value, it->value_len)) - return -1; - if (ostream_append(fp, "\n", 1)) - return -1; - } - - return padd_file(fp, total_size); -} - -int write_tar_header(ostream_t *fp, const struct stat *sb, const char *name, - const char *slink_target, const tar_xattr_t *xattr, - unsigned int counter) -{ - const char *reason; - char buffer[64]; - int type; - - if (xattr != NULL) { - sprintf(buffer, "pax/xattr%u", counter); - - if (write_schily_xattr(fp, sb, buffer, xattr)) - return -1; - } - - if (!S_ISLNK(sb->st_mode)) - slink_target = NULL; - - if (S_ISLNK(sb->st_mode) && sb->st_size >= 100) { - sprintf(buffer, "gnu/target%u", counter); - if (write_gnu_header(fp, sb, slink_target, sb->st_size, - TAR_TYPE_GNU_SLINK, buffer)) - return -1; - slink_target = NULL; - } - - if (strlen(name) >= 100) { - sprintf(buffer, "gnu/name%u", counter); - - if (write_gnu_header(fp, sb, name, strlen(name), - TAR_TYPE_GNU_PATH, buffer)) { - return -1; - } - - sprintf(buffer, "gnu/data%u", counter); - name = buffer; - } - - switch (sb->st_mode & S_IFMT) { - case S_IFCHR: type = TAR_TYPE_CHARDEV; break; - case S_IFBLK: type = TAR_TYPE_BLOCKDEV; break; - case S_IFLNK: type = TAR_TYPE_SLINK; break; - case S_IFREG: type = TAR_TYPE_FILE; break; - case S_IFDIR: type = TAR_TYPE_DIR; break; - case S_IFIFO: type = TAR_TYPE_FIFO; break; - case S_IFSOCK: - reason = "cannot pack socket"; - goto out_skip; - default: - reason = "unknown type"; - goto out_skip; - } - - return write_header(fp, sb, name, slink_target, type); -out_skip: - fprintf(stderr, "WARNING: %s: %s\n", name, reason); - return 1; -} - -int write_hard_link(ostream_t *fp, const struct stat *sb, const char *name, - const char *target, unsigned int counter) -{ - tar_header_t hdr; - char buffer[64]; - size_t len; - - memset(&hdr, 0, sizeof(hdr)); - - len = strlen(target); - if (len >= 100) { - sprintf(buffer, "gnu/target%u", counter); - if (write_gnu_header(fp, sb, target, len, - TAR_TYPE_GNU_SLINK, buffer)) - return -1; - sprintf(hdr.linkname, "hardlink_%u", counter); - } else { - memcpy(hdr.linkname, target, len); - } - - len = strlen(name); - if (len >= 100) { - sprintf(buffer, "gnu/name%u", counter); - if (write_gnu_header(fp, sb, name, len, - TAR_TYPE_GNU_PATH, buffer)) { - return -1; - } - sprintf(hdr.name, "gnu/data%u", counter); - } else { - memcpy(hdr.name, name, len); - } - - write_number(hdr.mode, sb->st_mode & ~S_IFMT, sizeof(hdr.mode)); - write_number(hdr.uid, sb->st_uid, sizeof(hdr.uid)); - write_number(hdr.gid, sb->st_gid, sizeof(hdr.gid)); - write_number(hdr.size, 0, sizeof(hdr.size)); - write_number_signed(hdr.mtime, sb->st_mtime, sizeof(hdr.mtime)); - hdr.typeflag = TAR_TYPE_LINK; - memcpy(hdr.magic, TAR_MAGIC_OLD, sizeof(hdr.magic)); - memcpy(hdr.version, TAR_VERSION_OLD, sizeof(hdr.version)); - sprintf(hdr.uname, "%u", sb->st_uid); - sprintf(hdr.gname, "%u", sb->st_gid); - write_number(hdr.devmajor, 0, sizeof(hdr.devmajor)); - write_number(hdr.devminor, 0, sizeof(hdr.devminor)); - - update_checksum(&hdr); - return ostream_append(fp, &hdr, sizeof(hdr)); -} diff --git a/lib/util/Makemodule.am b/lib/util/Makemodule.am index ec38b7a..35e8078 100644 --- a/lib/util/Makemodule.am +++ b/lib/util/Makemodule.am @@ -1,21 +1,14 @@ -libutil_a_SOURCES = include/util/util.h include/util/str_table.h -libutil_a_SOURCES += include/util/hash_table.h include/util/test.h -libutil_a_SOURCES += lib/util/str_table.c lib/util/alloc.c -libutil_a_SOURCES += lib/util/rbtree.c include/util/rbtree.h -libutil_a_SOURCES += lib/util/array.c include/util/array.h -libutil_a_SOURCES += lib/util/xxhash.c lib/util/hash_table.c -libutil_a_SOURCES += lib/util/fast_urem_by_const.h -libutil_a_SOURCES += include/util/threadpool.h -libutil_a_SOURCES += include/util/w32threadwrap.h -libutil_a_SOURCES += lib/util/threadpool_serial.c -libutil_a_SOURCES += lib/util/is_memory_zero.c -libutil_a_SOURCES += lib/util/mkdir_p.c -libutil_a_SOURCES += lib/util/canonicalize_name.c -libutil_a_SOURCES += lib/util/filename_sane.c -libutil_a_SOURCES += lib/util/source_date_epoch.c -libutil_a_SOURCES += lib/util/file_cmp.c -libutil_a_SOURCES += lib/util/hex_decode.c -libutil_a_SOURCES += lib/util/base64_decode.c +libutil_a_SOURCES = include/util/util.h include/util/str_table.h \ + include/util/hash_table.h include/util/test.h include/util/rbtree.h \ + include/util/array.h include/util/threadpool.h \ + include/util/w32threadwrap.h include/util/mempool.h \ + lib/util/src/str_table.c lib/util/src/alloc.c lib/util/src/rbtree.c \ + lib/util/src/array.c lib/util/src/xxhash.c lib/util/src/hash_table.c \ + lib/util/src/fast_urem_by_const.h lib/util/src/threadpool_serial.c \ + lib/util/src/is_memory_zero.c lib/util/src/mkdir_p.c \ + lib/util/src/canonicalize_name.c lib/util/src/filename_sane.c \ + lib/util/src/source_date_epoch.c lib/util/src/file_cmp.c \ + lib/util/src/hex_decode.c lib/util/src/base64_decode.c libutil_a_CFLAGS = $(AM_CFLAGS) libutil_a_CPPFLAGS = $(AM_CPPFLAGS) @@ -24,18 +17,18 @@ libutil_a_CFLAGS += -DWINVER=0x0600 -D_WIN32_WINNT=0x0600 endif if HAVE_PTHREAD -libutil_a_SOURCES += lib/util/threadpool.c +libutil_a_SOURCES += lib/util/src/threadpool.c libutil_a_CFLAGS += $(PTHREAD_CFLAGS) else if WINDOWS -libutil_a_SOURCES += lib/util/threadpool.c +libutil_a_SOURCES += lib/util/src/threadpool.c else libutil_a_CPPFLAGS += -DNO_THREAD_IMPL endif endif if CUSTOM_ALLOC -libutil_a_SOURCES += lib/util/mempool.c include/util/mempool.h +libutil_a_SOURCES += lib/util/src/mempool.c endif noinst_LIBRARIES += libutil.a diff --git a/lib/util/alloc.c b/lib/util/alloc.c deleted file mode 100644 index 359fef5..0000000 --- a/lib/util/alloc.c +++ /dev/null @@ -1,37 +0,0 @@ -/* SPDX-License-Identifier: LGPL-3.0-or-later */ -/* - * alloc.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "config.h" - -#include "util/util.h" - -#include -#include - -void *alloc_flex(size_t base_size, size_t item_size, size_t nmemb) -{ - size_t size; - - if (SZ_MUL_OV(nmemb, item_size, &size) || - SZ_ADD_OV(base_size, size, &size)) { - errno = EOVERFLOW; - return NULL; - } - - return calloc(1, size); -} - -void *alloc_array(size_t item_size, size_t nmemb) -{ - size_t size; - - if (SZ_MUL_OV(nmemb, item_size, &size)) { - errno = EOVERFLOW; - return NULL; - } - - return calloc(1, size); -} diff --git a/lib/util/array.c b/lib/util/array.c deleted file mode 100644 index 40bac50..0000000 --- a/lib/util/array.c +++ /dev/null @@ -1,115 +0,0 @@ -/* SPDX-License-Identifier: LGPL-3.0-or-later */ -/* - * array.c - * - * Copyright (C) 2021 David Oberhollenzer - */ -#include "config.h" -#include "compat.h" -#include "util/array.h" - -#include "sqfs/error.h" - -#include - -int array_init(array_t *array, size_t size, size_t capacity) -{ - size_t total; - - memset(array, 0, sizeof(*array)); - - if (capacity > 0) { - if (SZ_MUL_OV(size, capacity, &total)) - return SQFS_ERROR_OVERFLOW; - - array->data = malloc(total); - if (array->data == NULL) - return SQFS_ERROR_ALLOC; - } - - array->size = size; - array->count = capacity; - return 0; -} - -int array_init_copy(array_t *array, const array_t *src) -{ - int ret; - - ret = array_init(array, src->size, src->used); - if (ret != 0) - return ret; - - memcpy(array->data, src->data, src->used * src->size); - array->used = src->used; - return 0; -} - -void array_cleanup(array_t *array) -{ - free(array->data); - memset(array, 0, sizeof(*array)); -} - -int array_append(array_t *array, const void *data) -{ - size_t new_sz, new_count; - void *new; - - if (array->used == array->count) { - if (array->count == 0) { - new_count = 128; - } else { - if (SZ_MUL_OV(array->count, 2, &new_count)) - return SQFS_ERROR_ALLOC; - } - - if (SZ_MUL_OV(new_count, array->size, &new_sz)) - return SQFS_ERROR_ALLOC; - - new = realloc(array->data, new_sz); - if (new == NULL) - return SQFS_ERROR_ALLOC; - - array->data = new; - array->count = new_count; - } - - memcpy((char *)array->data + array->size * array->used, - data, array->size); - - array->used += 1; - return 0; -} - -int array_set_capacity(array_t *array, size_t capacity) -{ - size_t new_sz, new_count; - void *new; - - if (capacity <= array->count) - return 0; - - if (array->count == 0) { - new_count = 128; - } else { - if (SZ_MUL_OV(array->count, 2, &new_count)) - return SQFS_ERROR_ALLOC; - } - - while (new_count < capacity) { - if (SZ_MUL_OV(new_count, 2, &new_count)) - return SQFS_ERROR_ALLOC; - } - - if (SZ_MUL_OV(new_count, array->size, &new_sz)) - return SQFS_ERROR_ALLOC; - - new = realloc(array->data, new_sz); - if (new == NULL) - return SQFS_ERROR_ALLOC; - - array->data = new; - array->count = new_count; - return 0; -} diff --git a/lib/util/base64_decode.c b/lib/util/base64_decode.c deleted file mode 100644 index b1cf5b6..0000000 --- a/lib/util/base64_decode.c +++ /dev/null @@ -1,103 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * base64_decode.c - * - * Copyright (C) 2022 David Oberhollenzer - */ -#include "config.h" -#include "util/util.h" -#include "util/test.h" - -#include - -static int base64_digit(int c) -{ - if (isupper(c)) - return c - 'A'; - if (islower(c)) - return c - 'a' + 26; - if (isdigit(c)) - return c - '0' + 52; - if (c == '+') - return 62; - if (c == '/' || c == '-') - return 63; - return -1; -} - -int base64_decode(const char *in, size_t in_len, sqfs_u8 *out, size_t *out_len) -{ - int i1, i2, i3, i4; - size_t count = 0; - - while (in_len >= 4) { - i1 = base64_digit(*(in++)); - i2 = base64_digit(*(in++)); - i3 = *(in++); - i4 = *(in++); - in_len -= 4; - - if (i1 < 0 || i2 < 0 || count >= *out_len) - goto fail; - - out[count++] = (i1 << 2) | (i2 >> 4); - - if (i3 == '=' || i3 == '_') { - if ((i4 != '=' && i4 != '_') || in_len > 0) - goto fail; - break; - } - - i3 = base64_digit(i3); - if (i3 < 0 || count >= *out_len) - goto fail; - - out[count++] = ((i2 & 0x0F) << 4) | (i3 >> 2); - - if (i4 == '=' || i4 == '_') { - if (in_len > 0) - goto fail; - break; - } - - i4 = base64_digit(i4); - if (i4 < 0 || count >= *out_len) - goto fail; - - out[count++] = ((i3 & 0x3) << 6) | i4; - } - - /* libarchive has this bizarre bastardization of truncated base64 */ - if (in_len > 0) { - if (in_len == 1) - goto fail; - - i1 = base64_digit(*(in++)); - i2 = base64_digit(*(in++)); - in_len -= 2; - - if (i1 < 0 || i2 < 0 || count >= *out_len) - goto fail; - - out[count++] = (i1 << 2) | (i2 >> 4); - - if (in_len > 0) { - i3 = *(in++); - --in_len; - - if (i3 != '=' && i3 != '_') { - i3 = base64_digit(i3); - if (i3 < 0 || count >= *out_len) - goto fail; - - out[count++] = ((i2 & 0x0F) << 4) | (i3 >> 2); - } - } - } - - *out_len = count; - return 0; -fail: - *out_len = 0; - return -1; -} diff --git a/lib/util/canonicalize_name.c b/lib/util/canonicalize_name.c deleted file mode 100644 index 534e89e..0000000 --- a/lib/util/canonicalize_name.c +++ /dev/null @@ -1,60 +0,0 @@ -/* SPDX-License-Identifier: LGPL-3.0-or-later */ -/* - * canonicalize_name.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "config.h" -#include "util/util.h" - -static void normalize_slashes(char *filename) -{ - char *dst = filename, *src = filename; - - while (*src == '/') - ++src; - - while (*src != '\0') { - if (*src == '/') { - while (*src == '/') - ++src; - if (*src == '\0') - break; - *(dst++) = '/'; - } else { - *(dst++) = *(src++); - } - } - - *dst = '\0'; -} - -int canonicalize_name(char *filename) -{ - char *dst = filename, *src = filename; - - normalize_slashes(filename); - - while (*src != '\0') { - if (src[0] == '.') { - if (src[1] == '\0') - break; - if (src[1] == '/') { - src += 2; - continue; - } - if (src[1] == '.' && (src[2] == '/' || src[2] == '\0')) - return -1; - } - - while (*src != '\0' && *src != '/') - *(dst++) = *(src++); - - if (*src == '/') - *(dst++) = *(src++); - } - - *dst = '\0'; - normalize_slashes(filename); - return 0; -} diff --git a/lib/util/fast_urem_by_const.h b/lib/util/fast_urem_by_const.h deleted file mode 100644 index 4fb78d3..0000000 --- a/lib/util/fast_urem_by_const.h +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright © 2010 Valve Software - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ - -#include "sqfs/predef.h" - -#include -#include - -/* - * Code for fast 32-bit unsigned remainder, based off of "Faster Remainder by - * Direct Computation: Applications to Compilers and Software Libraries," - * available at https://arxiv.org/pdf/1902.01961.pdf. - * - * util_fast_urem32(n, d, REMAINDER_MAGIC(d)) returns the same thing as - * n % d for any unsigned n and d, however it compiles down to only a few - * multiplications, so it should be faster than plain sqfs_u32 modulo if the - * same divisor is used many times. - */ - -#define REMAINDER_MAGIC(divisor) \ - ((sqfs_u64) ~0ull / (divisor) + 1) - -/* - * Get bits 64-96 of a 32x64-bit multiply. If __int128_t is available, we use - * it, which usually compiles down to one instruction on 64-bit architectures. - * Otherwise on 32-bit architectures we usually get four instructions (one - * 32x32->64 multiply, one 32x32->32 multiply, and one 64-bit add). - */ - -static inline sqfs_u32 -_mul32by64_hi(sqfs_u32 a, sqfs_u64 b) -{ -#if defined(__SIZEOF_INT128__) && __SIZEOF_INT128__ == 16 - return ((__uint128_t) b * a) >> 64; -#else - /* - * Let b = b0 + 2^32 * b1. Then a * b = a * b0 + 2^32 * a * b1. We would - * have to do a 96-bit addition to get the full result, except that only - * one term has non-zero lower 32 bits, which means that to get the high 32 - * bits, we only have to add the high 64 bits of each term. Unfortunately, - * we have to do the 64-bit addition in case the low 32 bits overflow. - */ - sqfs_u32 b0 = (sqfs_u32) b; - sqfs_u32 b1 = b >> 32; - return ((((sqfs_u64) a * b0) >> 32) + (sqfs_u64) a * b1) >> 32; -#endif -} - -static inline sqfs_u32 -util_fast_urem32(sqfs_u32 n, sqfs_u32 d, sqfs_u64 magic) -{ - sqfs_u64 lowbits = magic * n; - sqfs_u32 result = _mul32by64_hi(d, lowbits); - assert(result == n % d); - return result; -} - diff --git a/lib/util/file_cmp.c b/lib/util/file_cmp.c deleted file mode 100644 index 2aa0cc2..0000000 --- a/lib/util/file_cmp.c +++ /dev/null @@ -1,41 +0,0 @@ -/* SPDX-License-Identifier: LGPL-3.0-or-later */ -/* - * file_cmp.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "config.h" - -#include "util/util.h" -#include "sqfs/io.h" - -#include - -int check_file_range_equal(sqfs_file_t *file, void *scratch, size_t scratch_sz, - sqfs_u64 loc_a, sqfs_u64 loc_b, sqfs_u64 size) -{ - sqfs_u8 *ptr_a = scratch, *ptr_b = ptr_a + scratch_sz / 2; - int ret; - - while (size > 0) { - size_t diff = scratch_sz / 2; - diff = (sqfs_u64)diff > size ? size : diff; - - ret = file->read_at(file, loc_a, ptr_a, diff); - if (ret != 0) - return ret; - - ret = file->read_at(file, loc_b, ptr_b, diff); - if (ret != 0) - return ret; - - if (memcmp(ptr_a, ptr_b, diff) != 0) - return 1; - - size -= diff; - loc_a += diff; - loc_b += diff; - } - - return 0; -} diff --git a/lib/util/filename_sane.c b/lib/util/filename_sane.c deleted file mode 100644 index b52ce4d..0000000 --- a/lib/util/filename_sane.c +++ /dev/null @@ -1,78 +0,0 @@ -/* SPDX-License-Identifier: LGPL-3.0-or-later */ -/* - * filename_sane.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "config.h" -#include "util/util.h" - -#include - -#if defined(_WIN32) || defined(__WINDOWS__) || defined(TEST_WIN32) -#ifdef _MSC_VER -#define strncasecmp _strnicmp -#define strcasecmp _stricmp -#endif - -static const char *bad_names[] = { - "CON", "PRN", "AUX", "NUL", - "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9", - "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9", -}; - -static bool is_allowed_by_os(const char *name) -{ - size_t len, i; - - for (i = 0; i < sizeof(bad_names) / sizeof(bad_names[0]); ++i) { - len = strlen(bad_names[i]); - - if (strncasecmp(name, bad_names[i], len) != 0) - continue; - - if (name[len] == '\0') - return false; - - if (name[len] == '.' && strchr(name + len + 1, '.') == NULL) - return false; - } - - return true; -} -#else -static bool is_allowed_by_os(const char *name) -{ - (void)name; - return true; -} -#endif - -bool is_filename_sane(const char *name, bool check_os_specific) -{ - if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0) - return false; - - if (check_os_specific && !is_allowed_by_os(name)) - return false; - - while (*name != '\0') { - if (*name == '/') - return false; - -#if defined(_WIN32) || defined(__WINDOWS__) || defined(TEST_WIN32) - if (check_os_specific) { - if (*name == '<' || *name == '>' || *name == ':') - return false; - if (*name == '"' || *name == '|' || *name == '?') - return false; - if (*name == '*' || *name == '\\' || *name <= 31) - return false; - } -#endif - - ++name; - } - - return true; -} diff --git a/lib/util/hash_table.c b/lib/util/hash_table.c deleted file mode 100644 index 0010e9f..0000000 --- a/lib/util/hash_table.c +++ /dev/null @@ -1,417 +0,0 @@ -/* - * Copyright © 2009,2012 Intel Corporation - * Copyright © 1988-2004 Keith Packard and Bart Massey. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - * - * Except as contained in this notice, the names of the authors - * or their institutions shall not be used in advertising or - * otherwise to promote the sale, use or other dealings in this - * Software without prior written authorization from the - * authors. - * - * Authors: - * Eric Anholt - * Keith Packard - */ - -/** - * Implements an open-addressing, linear-reprobing hash table. - * - * For more information, see: - * - * http://cgit.freedesktop.org/~anholt/hash_table/tree/README - */ - -#include -#include -#include - -#include "fast_urem_by_const.h" -#include "util/hash_table.h" - -# define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) - -static const sqfs_u32 deleted_key_value; - -/** - * From Knuth -- a good choice for hash/rehash values is p, p-2 where - * p and p-2 are both prime. These tables are sized to have an extra 10% - * free to avoid exponential performance degradation as the hash table fills - */ -static const struct { - sqfs_u32 max_entries, size, rehash; - sqfs_u64 size_magic, rehash_magic; -} hash_sizes[] = { -#define ENTRY(max_entries, size, rehash) \ - { max_entries, size, rehash, \ - REMAINDER_MAGIC(size), REMAINDER_MAGIC(rehash) } - - ENTRY(2, 5, 3 ), - ENTRY(4, 7, 5 ), - ENTRY(8, 13, 11 ), - ENTRY(16, 19, 17 ), - ENTRY(32, 43, 41 ), - ENTRY(64, 73, 71 ), - ENTRY(128, 151, 149 ), - ENTRY(256, 283, 281 ), - ENTRY(512, 571, 569 ), - ENTRY(1024, 1153, 1151 ), - ENTRY(2048, 2269, 2267 ), - ENTRY(4096, 4519, 4517 ), - ENTRY(8192, 9013, 9011 ), - ENTRY(16384, 18043, 18041 ), - ENTRY(32768, 36109, 36107 ), - ENTRY(65536, 72091, 72089 ), - ENTRY(131072, 144409, 144407 ), - ENTRY(262144, 288361, 288359 ), - ENTRY(524288, 576883, 576881 ), - ENTRY(1048576, 1153459, 1153457 ), - ENTRY(2097152, 2307163, 2307161 ), - ENTRY(4194304, 4613893, 4613891 ), - ENTRY(8388608, 9227641, 9227639 ), - ENTRY(16777216, 18455029, 18455027 ), - ENTRY(33554432, 36911011, 36911009 ), - ENTRY(67108864, 73819861, 73819859 ), - ENTRY(134217728, 147639589, 147639587 ), - ENTRY(268435456, 295279081, 295279079 ), - ENTRY(536870912, 590559793, 590559791 ), - ENTRY(1073741824, 1181116273, 1181116271 ), - ENTRY(2147483648ul, 2362232233ul, 2362232231ul ) -}; - -static inline bool -key_pointer_is_reserved(const struct hash_table *ht, const void *key) -{ - return key == NULL || key == ht->deleted_key; -} - -static int -entry_is_free(const struct hash_entry *entry) -{ - return entry->key == NULL; -} - -static int -entry_is_deleted(const struct hash_table *ht, struct hash_entry *entry) -{ - return entry->key == ht->deleted_key; -} - -static int -entry_is_present(const struct hash_table *ht, struct hash_entry *entry) -{ - return entry->key != NULL && entry->key != ht->deleted_key; -} - -static bool -hash_table_init(struct hash_table *ht, - sqfs_u32 (*key_hash_function)(void *user, const void *key), - bool (*key_equals_function)(void *user, const void *a, - const void *b)) -{ - ht->size_index = 0; - ht->size = hash_sizes[ht->size_index].size; - ht->rehash = hash_sizes[ht->size_index].rehash; - ht->size_magic = hash_sizes[ht->size_index].size_magic; - ht->rehash_magic = hash_sizes[ht->size_index].rehash_magic; - ht->max_entries = hash_sizes[ht->size_index].max_entries; - ht->key_hash_function = key_hash_function; - ht->key_equals_function = key_equals_function; - ht->table = calloc(sizeof(struct hash_entry), ht->size); - ht->entries = 0; - ht->deleted_entries = 0; - ht->deleted_key = &deleted_key_value; - - return ht->table != NULL; -} - -struct hash_table * -hash_table_create(sqfs_u32 (*key_hash_function)(void *user, const void *key), - bool (*key_equals_function)(void *user, const void *a, - const void *b)) -{ - struct hash_table *ht; - - ht = malloc(sizeof(struct hash_table)); - if (ht == NULL) - return NULL; - - if (!hash_table_init(ht, key_hash_function, key_equals_function)) { - free(ht); - return NULL; - } - - return ht; -} - -struct hash_table * -hash_table_clone(struct hash_table *src) -{ - struct hash_table *ht; - - ht = malloc(sizeof(struct hash_table)); - if (ht == NULL) - return NULL; - - memcpy(ht, src, sizeof(struct hash_table)); - - ht->table = calloc(sizeof(struct hash_entry), ht->size); - if (ht->table == NULL) { - free(ht); - return NULL; - } - - memcpy(ht->table, src->table, ht->size * sizeof(struct hash_entry)); - - return ht; -} - -/** - * Frees the given hash table. - */ -void -hash_table_destroy(struct hash_table *ht, - void (*delete_function)(struct hash_entry *entry)) -{ - if (!ht) - return; - - if (delete_function) { - hash_table_foreach(ht, entry) { - delete_function(entry); - } - } - free(ht->table); - free(ht); -} - -static struct hash_entry * -hash_table_search(struct hash_table *ht, sqfs_u32 hash, const void *key) -{ - assert(!key_pointer_is_reserved(ht, key)); - - sqfs_u32 size = ht->size; - sqfs_u32 start_hash_address = util_fast_urem32(hash, size, ht->size_magic); - sqfs_u32 double_hash = 1 + util_fast_urem32(hash, ht->rehash, - ht->rehash_magic); - sqfs_u32 hash_address = start_hash_address; - - do { - struct hash_entry *entry = ht->table + hash_address; - - if (entry_is_free(entry)) { - return NULL; - } else if (entry_is_present(ht, entry) && entry->hash == hash) { - if (ht->key_equals_function(ht->user, key, entry->key)) { - return entry; - } - } - - hash_address += double_hash; - if (hash_address >= size) - hash_address -= size; - } while (hash_address != start_hash_address); - - return NULL; -} - -/** - * Finds a hash table entry with the given key and hash of that key. - * - * Returns NULL if no entry is found. Note that the data pointer may be - * modified by the user. - */ -struct hash_entry * -hash_table_search_pre_hashed(struct hash_table *ht, sqfs_u32 hash, - const void *key) -{ - assert(ht->key_hash_function == NULL || - hash == ht->key_hash_function(ht->user, key)); - return hash_table_search(ht, hash, key); -} - -static void -hash_table_insert_rehash(struct hash_table *ht, sqfs_u32 hash, - const void *key, void *data) -{ - sqfs_u32 size = ht->size; - sqfs_u32 start_hash_address = util_fast_urem32(hash, size, ht->size_magic); - sqfs_u32 double_hash = 1 + util_fast_urem32(hash, ht->rehash, - ht->rehash_magic); - sqfs_u32 hash_address = start_hash_address; - do { - struct hash_entry *entry = ht->table + hash_address; - - if (entry->key == NULL) { - entry->hash = hash; - entry->key = key; - entry->data = data; - return; - } - - hash_address += double_hash; - if (hash_address >= size) - hash_address -= size; - } while (true); -} - -static void -hash_table_rehash(struct hash_table *ht, unsigned new_size_index) -{ - struct hash_table old_ht; - struct hash_entry *table; - - if (new_size_index >= ARRAY_SIZE(hash_sizes)) - return; - - table = calloc(sizeof(struct hash_entry), hash_sizes[new_size_index].size); - if (table == NULL) - return; - - old_ht = *ht; - - ht->table = table; - ht->size_index = new_size_index; - ht->size = hash_sizes[ht->size_index].size; - ht->rehash = hash_sizes[ht->size_index].rehash; - ht->size_magic = hash_sizes[ht->size_index].size_magic; - ht->rehash_magic = hash_sizes[ht->size_index].rehash_magic; - ht->max_entries = hash_sizes[ht->size_index].max_entries; - ht->entries = 0; - ht->deleted_entries = 0; - - hash_table_foreach(&old_ht, entry) { - hash_table_insert_rehash(ht, entry->hash, entry->key, entry->data); - } - - ht->entries = old_ht.entries; - - free(old_ht.table); -} - -static struct hash_entry * -hash_table_insert(struct hash_table *ht, sqfs_u32 hash, - const void *key, void *data) -{ - struct hash_entry *available_entry = NULL; - - assert(!key_pointer_is_reserved(ht, key)); - - if (ht->entries >= ht->max_entries) { - hash_table_rehash(ht, ht->size_index + 1); - } else if (ht->deleted_entries + ht->entries >= ht->max_entries) { - hash_table_rehash(ht, ht->size_index); - } - - sqfs_u32 size = ht->size; - sqfs_u32 start_hash_address = util_fast_urem32(hash, size, ht->size_magic); - sqfs_u32 double_hash = 1 + util_fast_urem32(hash, ht->rehash, - ht->rehash_magic); - sqfs_u32 hash_address = start_hash_address; - do { - struct hash_entry *entry = ht->table + hash_address; - - if (!entry_is_present(ht, entry)) { - /* Stash the first available entry we find */ - if (available_entry == NULL) - available_entry = entry; - if (entry_is_free(entry)) - break; - } - - /* Implement replacement when another insert happens - * with a matching key. This is a relatively common - * feature of hash tables, with the alternative - * generally being "insert the new value as well, and - * return it first when the key is searched for". - * - * Note that the hash table doesn't have a delete - * callback. If freeing of old data pointers is - * required to avoid memory leaks, perform a search - * before inserting. - */ - if (!entry_is_deleted(ht, entry) && - entry->hash == hash && - ht->key_equals_function(ht->user, key, entry->key)) { - entry->key = key; - entry->data = data; - return entry; - } - - hash_address += double_hash; - if (hash_address >= size) - hash_address -= size; - } while (hash_address != start_hash_address); - - if (available_entry) { - if (entry_is_deleted(ht, available_entry)) - ht->deleted_entries--; - available_entry->hash = hash; - available_entry->key = key; - available_entry->data = data; - ht->entries++; - return available_entry; - } - - /* We could hit here if a required resize failed. An unchecked-malloc - * application could ignore this result. - */ - return NULL; -} - -/** - * Inserts the key with the given hash into the table. - * - * Note that insertion may rearrange the table on a resize or rehash, - * so previously found hash_entries are no longer valid after this function. - */ -struct hash_entry * -hash_table_insert_pre_hashed(struct hash_table *ht, sqfs_u32 hash, - const void *key, void *data) -{ - assert(ht->key_hash_function == NULL || - hash == ht->key_hash_function(ht->user, key)); - return hash_table_insert(ht, hash, key, data); -} - -/** - * This function is an iterator over the hash table. - * - * Pass in NULL for the first entry, as in the start of a for loop. Note that - * an iteration over the table is O(table_size) not O(entries). - */ -struct hash_entry * -hash_table_next_entry(struct hash_table *ht, - struct hash_entry *entry) -{ - if (entry == NULL) - entry = ht->table; - else - entry = entry + 1; - - for (; entry != ht->table + ht->size; entry++) { - if (entry_is_present(ht, entry)) { - return entry; - } - } - - return NULL; -} diff --git a/lib/util/hex_decode.c b/lib/util/hex_decode.c deleted file mode 100644 index ee4b21c..0000000 --- a/lib/util/hex_decode.c +++ /dev/null @@ -1,34 +0,0 @@ -/* SPDX-License-Identifier: LGPL-3.0-or-later */ -/* - * hex_decode.h - * - * Copyright (C) 2022 David Oberhollenzer - */ -#include "util/util.h" - -#include - -static sqfs_u8 xdigit(int in) -{ - if (isupper(in)) - return in - 'A' + 10; - if (islower(in)) - return in - 'a' + 10; - return in - '0'; -} - -int hex_decode(const char *in, size_t in_sz, sqfs_u8 *out, size_t out_sz) -{ - while (out_sz > 0 && in_sz >= 2 && - isxdigit(in[0]) && isxdigit(in[1])) { - sqfs_u8 hi = xdigit(*(in++)); - sqfs_u8 lo = xdigit(*(in++)); - - *(out++) = (hi << 4) | lo; - - in_sz -= 2; - --out_sz; - } - - return (in_sz > 0) ? -1 : 0; -} diff --git a/lib/util/is_memory_zero.c b/lib/util/is_memory_zero.c deleted file mode 100644 index aabd45d..0000000 --- a/lib/util/is_memory_zero.c +++ /dev/null @@ -1,54 +0,0 @@ -/* SPDX-License-Identifier: LGPL-3.0-or-later */ -/* - * is_memory_zero.c - * - * Copyright (C) 2021 David Oberhollenzer - */ -#include "config.h" -#include "util/util.h" - -#include - -#define U64THRESHOLD (128) - -static bool test_u8(const unsigned char *blob, size_t size) -{ - while (size--) { - if (*(blob++) != 0) - return false; - } - - return true; -} - -bool is_memory_zero(const void *blob, size_t size) -{ - const sqfs_u64 *u64ptr; - size_t diff; - - if (size < U64THRESHOLD) - return test_u8(blob, size); - - diff = (uintptr_t)blob % sizeof(sqfs_u64); - - if (diff != 0) { - diff = sizeof(sqfs_u64) - diff; - - if (!test_u8(blob, diff)) - return false; - - blob = (const char *)blob + diff; - size -= diff; - } - - u64ptr = blob; - - while (size >= sizeof(sqfs_u64)) { - if (*(u64ptr++) != 0) - return false; - - size -= sizeof(sqfs_u64); - } - - return test_u8((const unsigned char *)u64ptr, size); -} diff --git a/lib/util/mempool.c b/lib/util/mempool.c deleted file mode 100644 index e2ddaf0..0000000 --- a/lib/util/mempool.c +++ /dev/null @@ -1,216 +0,0 @@ -/* SPDX-License-Identifier: LGPL-3.0-or-later */ -/* - * mempool.c - * - * Copyright (C) 2021 David Oberhollenzer - */ -#include "util/mempool.h" - -#include -#include -#include -#include - -#if defined(_WIN32) || defined(__WINDOWS__) -#define WIN32_LEAN_AND_MEAN -#include -#else -#include -#endif - -#define DEF_POOL_SIZE (65536) -#define MEM_ALIGN (8) - -typedef struct pool_t { - struct pool_t *next; - - unsigned char *data; - unsigned char *limit; - - unsigned int *bitmap; - - size_t obj_free; - - unsigned int blob[]; -} pool_t; - -struct mem_pool_t { - size_t obj_size; - size_t pool_size; - size_t bitmap_count; - pool_t *pool_list; -}; - -static size_t pool_size_from_bitmap_count(size_t count, size_t obj_size) -{ - size_t size, byte_count, bit_count; - - size = sizeof(pool_t); - if (size % sizeof(unsigned int)) - size += sizeof(unsigned int) - size % sizeof(unsigned int); - - byte_count = count * sizeof(unsigned int); - bit_count = byte_count * CHAR_BIT; - - size += byte_count; - if (size % obj_size) - size += obj_size - size % obj_size; - - size += bit_count * obj_size; - return size; -} - -static pool_t *create_pool(const mem_pool_t *mem) -{ - unsigned char *ptr; - pool_t *pool; - -#if defined(_WIN32) || defined(__WINDOWS__) - pool = VirtualAlloc(NULL, mem->pool_size, MEM_RESERVE | MEM_COMMIT, - PAGE_READWRITE); - - if (pool == NULL) - return NULL; -#else - pool = mmap(NULL, mem->pool_size, PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); - - if (pool == MAP_FAILED) - return NULL; -#endif - pool->bitmap = pool->blob; - pool->obj_free = mem->bitmap_count * sizeof(unsigned int) * CHAR_BIT; - - ptr = (unsigned char *)(pool->bitmap + mem->bitmap_count); - - if (((uintptr_t)ptr) % mem->obj_size) { - ptr += mem->obj_size; - ptr -= ((uintptr_t)ptr) % mem->obj_size; - } - - pool->data = ptr; - pool->limit = pool->data + pool->obj_free * mem->obj_size - 1; - - memset(pool->bitmap, 0, mem->bitmap_count * sizeof(unsigned int)); - return pool; -} - -mem_pool_t *mem_pool_create(size_t obj_size) -{ - mem_pool_t *mem = calloc(1, sizeof(*mem)); - size_t count = 1, total; - - if (mem == NULL) - return NULL; - - if (obj_size % MEM_ALIGN) - obj_size += MEM_ALIGN - obj_size % MEM_ALIGN; - - for (;;) { - total = pool_size_from_bitmap_count(count, obj_size); - if (total > DEF_POOL_SIZE) - break; - ++count; - } - - --count; - - mem->obj_size = obj_size; - mem->pool_size = DEF_POOL_SIZE; - mem->bitmap_count = count; - return mem; -} - -void mem_pool_destroy(mem_pool_t *mem) -{ - while (mem->pool_list != NULL) { - pool_t *pool = mem->pool_list; - mem->pool_list = pool->next; - -#if defined(_WIN32) || defined(__WINDOWS__) - VirtualFree(pool, mem->pool_size, MEM_RELEASE); -#else - munmap(pool, mem->pool_size); -#endif - } - - free(mem); -} - -void *mem_pool_allocate(mem_pool_t *mem) -{ - size_t idx, i, j; - void *ptr = NULL; - pool_t *it; -retry_pool: - for (it = mem->pool_list; it != NULL; it = it->next) { - if (it->obj_free > 0) - break; - } - - if (it == NULL) { - it = create_pool(mem); - if (it == NULL) - return NULL; - - it->next = mem->pool_list; - mem->pool_list = it; - } - - for (i = 0; i < mem->bitmap_count; ++i) { - if (it->bitmap[i] < UINT_MAX) - break; - } - - if (i == mem->bitmap_count) { - it->obj_free = 0; - goto retry_pool; - } - - for (j = 0; j < (sizeof(it->bitmap[i]) * CHAR_BIT); ++j) { - if (!(it->bitmap[i] & (1UL << j))) - break; - } - - if (j == (sizeof(it->bitmap[i]) * CHAR_BIT)) { - it->obj_free = 0; - goto retry_pool; - } - - idx = i * sizeof(unsigned int) * CHAR_BIT + j; - ptr = it->data + idx * mem->obj_size; - - it->bitmap[i] |= (1UL << j); - it->obj_free -= 1; - - memset(ptr, 0, mem->obj_size); - return ptr; -} - -void mem_pool_free(mem_pool_t *mem, void *ptr) -{ - size_t idx, i, j; - pool_t *it; - - for (it = mem->pool_list; it != NULL; it = it->next) { - if ((unsigned char *)ptr >= it->data && - (unsigned char *)ptr < it->limit) { - break; - } - } - - assert(it != NULL); - - idx = (size_t)((unsigned char *)ptr - it->data); - - assert((idx % mem->obj_size) == 0); - idx /= mem->obj_size; - - i = idx / (sizeof(unsigned int) * CHAR_BIT); - j = idx % (sizeof(unsigned int) * CHAR_BIT); - - assert((it->bitmap[i] & (1 << j)) != 0); - - it->bitmap[i] &= ~(1 << j); - it->obj_free += 1; -} diff --git a/lib/util/mkdir_p.c b/lib/util/mkdir_p.c deleted file mode 100644 index 993d8ec..0000000 --- a/lib/util/mkdir_p.c +++ /dev/null @@ -1,170 +0,0 @@ -/* SPDX-License-Identifier: LGPL-3.0-or-later */ -/* - * mkdir_p.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "util/util.h" - -#include -#include -#include -#include - -#ifdef _WIN32 -/* - Supported paths: - - :\ - - \\\\ - - \\?\:\ - - \\?\UNC\\\ - - Relative path not starting with '\' - */ - -static WCHAR *skip_unc_path(WCHAR *ptr) -{ - /* server */ - if (*ptr == '\0' || *ptr == '\\') - return NULL; - - while (*ptr != '\0' && *ptr != '\\') - ++ptr; - - if (*(ptr++) != '\\') - return NULL; - - /* share */ - if (*ptr == '\0' || *ptr == '\\') - return NULL; - - while (*ptr != '\0' && *ptr != '\\') - ++ptr; - - return (*ptr == '\\') ? (ptr + 1) : ptr; -} - -static WCHAR *skip_prefix(WCHAR *ptr) -{ - if (isalpha(ptr[0]) && ptr[1] == ':' && ptr[2] == '\\') - return ptr + 3; - - if (ptr[0] == '\\' && ptr[1] == '\\') { - if (ptr[2] == '?') { - if (ptr[3] != '\\') - return NULL; - - ptr += 4; - - if ((ptr[0] == 'u' || ptr[0] == 'U') && - (ptr[1] == 'n' || ptr[1] == 'N') && - (ptr[2] == 'c' || ptr[2] == 'C') && - ptr[3] == '\\') { - ptr += 4; - - return skip_unc_path(ptr); - } - - if (isalpha(ptr[0]) && ptr[1] == ':' && ptr[2] == '\\') - return ptr + 3; - - return NULL; - } - - return skip_unc_path(ptr); - } - - if (ptr[0] == '\\') - return NULL; - - return ptr; -} - -int mkdir_p(const char *path) -{ - WCHAR *wpath, *ptr, *end; - DWORD error; - bool done; - - - wpath = path_to_windows(path); - if (wpath == NULL) - return -1; - - ptr = skip_prefix(wpath); - if (ptr == NULL) { - fprintf(stderr, "Illegal or unsupported path: %s\n", path); - goto fail; - } - - while (*ptr != '\0') { - if (*ptr == '\\') { - ++ptr; - continue; - } - - for (end = ptr; *end != '\0' && *end != '\\'; ++end) - ++end; - - if (*end == '\\') { - done = false; - *end = '\0'; - } else { - done = true; - } - - if (!CreateDirectoryW(wpath, NULL)) { - error = GetLastError(); - - if (error != ERROR_ALREADY_EXISTS) { - fprintf(stderr, "Creating %s: %ld\n", - path, error); - goto fail; - } - } - - if (!done) - *end = '\\'; - - ptr = done ? end : (end + 1); - } - - free(wpath); - return 0; -fail: - free(wpath); - return -1; -} -#else -int mkdir_p(const char *path) -{ - size_t i, len; - char *buffer; - - while (path[0] == '/' && path[1] == '/') - ++path; - - if (*path == '\0' || (path[0] == '/' && path[1] == '\0')) - return 0; - - len = strlen(path) + 1; - buffer = alloca(len); - - for (i = 0; i < len; ++i) { - if (i > 0 && (path[i] == '/' || path[i] == '\0')) { - buffer[i] = '\0'; - - if (mkdir(buffer, 0755) != 0) { - if (errno != EEXIST) { - fprintf(stderr, "mkdir %s: %s\n", - buffer, strerror(errno)); - return -1; - } - } - } - - buffer[i] = path[i]; - } - - return 0; -} -#endif diff --git a/lib/util/rbtree.c b/lib/util/rbtree.c deleted file mode 100644 index 8b43e43..0000000 --- a/lib/util/rbtree.c +++ /dev/null @@ -1,267 +0,0 @@ -/* SPDX-License-Identifier: LGPL-3.0-or-later */ -/* - * rbtree.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "config.h" - -#include "sqfs/error.h" -#include "util/rbtree.h" - -#include -#include - -#define IS_RED(n) ((n) && (n)->is_red) - -#ifdef NO_CUSTOM_ALLOC -static void destroy_nodes_dfs(rbtree_node_t *n) -{ - rbtree_node_t *l, *r; - - if (n != NULL) { - l = n->left; - r = n->right; - free(n); - destroy_nodes_dfs(l); - destroy_nodes_dfs(r); - } -} -#else -static void destroy_nodes_dfs(rbtree_node_t *n) -{ - (void)n; -} -#endif - -static void flip_colors(rbtree_node_t *n) -{ - n->is_red = !n->is_red; - n->left->is_red = !n->left->is_red; - n->right->is_red = !n->right->is_red; -} - -static rbtree_node_t *rotate_right(rbtree_node_t *n) -{ - rbtree_node_t *x; - - x = n->left; - n->left = x->right; - x->right = n; - - x->is_red = x->right->is_red; - x->right->is_red = 1; - return x; -} - -static rbtree_node_t *rotate_left(rbtree_node_t *n) -{ - rbtree_node_t *x; - - x = n->right; - n->right = x->left; - x->left = n; - - x->is_red = x->left->is_red; - x->left->is_red = 1; - return x; -} - -static rbtree_node_t *subtree_balance(rbtree_node_t *n) -{ - if (IS_RED(n->right) && !IS_RED(n->left)) - n = rotate_left(n); - - if (IS_RED(n->left) && IS_RED(n->left->left)) - n = rotate_right(n); - - if (IS_RED(n->left) && IS_RED(n->right)) - flip_colors(n); - - return n; -} - -static rbtree_node_t *subtree_insert(rbtree_t *tree, rbtree_node_t *root, - rbtree_node_t *new) -{ - if (root == NULL) - return new; - - if (tree->key_compare(tree->key_context, new->data, root->data) < 0) { - root->left = subtree_insert(tree, root->left, new); - } else { - root->right = subtree_insert(tree, root->right, new); - } - - return subtree_balance(root); -} - -static rbtree_node_t *mknode(rbtree_t *t, const void *key, const void *value) -{ - rbtree_node_t *node; - -#ifdef NO_CUSTOM_ALLOC - node = calloc(1, sizeof(*node) + t->key_size_padded + t->value_size); -#else - node = mem_pool_allocate(t->pool); -#endif - - if (node == NULL) - return NULL; - - node->value_offset = t->key_size_padded; - node->is_red = 1; - - memcpy(node->data, key, t->key_size); - memcpy(node->data + t->key_size_padded, value, t->value_size); - return node; -} - -static rbtree_node_t *copy_node(rbtree_t *nt, const rbtree_t *t, - const rbtree_node_t *n) -{ - rbtree_node_t *out; - -#ifdef NO_CUSTOM_ALLOC - out = calloc(1, sizeof(*out) + t->key_size_padded + t->value_size); -#else - out = mem_pool_allocate(nt->pool); -#endif - - if (out == NULL) - return NULL; - - memcpy(out, n, sizeof(*n) + t->key_size_padded + t->value_size); - out->left = NULL; - out->right = NULL; - - if (n->left != NULL) { - out->left = copy_node(nt, t, n->left); - - if (out->left == NULL) { - destroy_nodes_dfs(out); - return NULL; - } - } - - if (n->right != NULL) { - out->right = copy_node(nt, t, n->right); - - if (out->right == NULL) { - destroy_nodes_dfs(out); - return NULL; - } - } - - return out; -} - -int rbtree_init(rbtree_t *tree, size_t keysize, size_t valuesize, - int(*key_compare)(const void *, const void *, const void *)) -{ - size_t diff, size; - - memset(tree, 0, sizeof(*tree)); - tree->key_compare = key_compare; - tree->key_size = keysize; - tree->key_size_padded = keysize; - tree->value_size = valuesize; - - /* make sure the value always has pointer alignment */ - diff = keysize % sizeof(void *); - - if (diff != 0) { - diff = sizeof(void *) - diff; - - if (SZ_ADD_OV(tree->key_size_padded, diff, - &tree->key_size_padded)) { - return SQFS_ERROR_OVERFLOW; - } - } - - /* make sure the node can store the offset */ - if (sizeof(size_t) > sizeof(sqfs_u32)) { - if (tree->key_size_padded > 0x0FFFFFFFFUL) - return SQFS_ERROR_OVERFLOW; - } - - /* make sure the nodes fit in memory */ - size = sizeof(rbtree_node_t); - - if (SZ_ADD_OV(size, tree->key_size_padded, &size)) - return SQFS_ERROR_OVERFLOW; - - if (SZ_ADD_OV(size, tree->value_size, &size)) - return SQFS_ERROR_OVERFLOW; - -#ifndef NO_CUSTOM_ALLOC - /* initialize the underlying pool allocator */ - tree->pool = mem_pool_create(size); - if (tree->pool == NULL) - return SQFS_ERROR_ALLOC; -#endif - return 0; -} - -int rbtree_copy(const rbtree_t *tree, rbtree_t *out) -{ - memcpy(out, tree, sizeof(*out)); - out->root = NULL; - -#ifndef NO_CUSTOM_ALLOC - out->pool = mem_pool_create(sizeof(rbtree_node_t) + - tree->key_size_padded + - tree->value_size); - if (out->pool == NULL) - return SQFS_ERROR_ALLOC; -#endif - - if (tree->root != NULL) { - out->root = copy_node(out, tree, tree->root); - - if (out->root == NULL) { - memset(out, 0, sizeof(*out)); - return SQFS_ERROR_ALLOC; - } - } - - return 0; -} - -void rbtree_cleanup(rbtree_t *tree) -{ -#ifdef NO_CUSTOM_ALLOC - destroy_nodes_dfs(tree->root); -#else - mem_pool_destroy(tree->pool); -#endif - memset(tree, 0, sizeof(*tree)); -} - -int rbtree_insert(rbtree_t *tree, const void *key, const void *value) -{ - rbtree_node_t *node = mknode(tree, key, value); - - if (node == NULL) - return SQFS_ERROR_ALLOC; - - tree->root = subtree_insert(tree, tree->root, node); - tree->root->is_red = 0; - return 0; -} - -rbtree_node_t *rbtree_lookup(const rbtree_t *tree, const void *key) -{ - rbtree_node_t *node = tree->root; - int ret; - - while (node != NULL) { - ret = tree->key_compare(tree->key_context, key, node->data); - if (ret == 0) - break; - - node = ret < 0 ? node->left : node->right; - } - - return node; -} diff --git a/lib/util/source_date_epoch.c b/lib/util/source_date_epoch.c deleted file mode 100644 index 26e5530..0000000 --- a/lib/util/source_date_epoch.c +++ /dev/null @@ -1,44 +0,0 @@ -/* SPDX-License-Identifier: LGPL-3.0-or-later */ -/* - * source_date_epoch.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "util/util.h" - -#include -#include -#include - -sqfs_u32 get_source_date_epoch(void) -{ - const char *str, *ptr; - sqfs_u32 x, tval = 0; - - str = getenv("SOURCE_DATE_EPOCH"); - - if (str == NULL || *str == '\0') - return 0; - - for (ptr = str; *ptr != '\0'; ++ptr) { - if (!isdigit(*ptr)) - goto fail_nan; - - x = (*ptr) - '0'; - - if (tval > (UINT32_MAX - x) / 10) - goto fail_ov; - - tval = tval * 10 + x; - } - - return tval; -fail_ov: - fprintf(stderr, "WARNING: SOURCE_DATE_EPOCH=%s does not fit into " - "32 bit integer\n", str); - return 0; -fail_nan: - fprintf(stderr, "WARNING: SOURCE_DATE_EPOCH=%s is not a positive " - "number\n", str); - return 0; -} diff --git a/lib/util/src/alloc.c b/lib/util/src/alloc.c new file mode 100644 index 0000000..359fef5 --- /dev/null +++ b/lib/util/src/alloc.c @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * alloc.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "config.h" + +#include "util/util.h" + +#include +#include + +void *alloc_flex(size_t base_size, size_t item_size, size_t nmemb) +{ + size_t size; + + if (SZ_MUL_OV(nmemb, item_size, &size) || + SZ_ADD_OV(base_size, size, &size)) { + errno = EOVERFLOW; + return NULL; + } + + return calloc(1, size); +} + +void *alloc_array(size_t item_size, size_t nmemb) +{ + size_t size; + + if (SZ_MUL_OV(nmemb, item_size, &size)) { + errno = EOVERFLOW; + return NULL; + } + + return calloc(1, size); +} diff --git a/lib/util/src/array.c b/lib/util/src/array.c new file mode 100644 index 0000000..40bac50 --- /dev/null +++ b/lib/util/src/array.c @@ -0,0 +1,115 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * array.c + * + * Copyright (C) 2021 David Oberhollenzer + */ +#include "config.h" +#include "compat.h" +#include "util/array.h" + +#include "sqfs/error.h" + +#include + +int array_init(array_t *array, size_t size, size_t capacity) +{ + size_t total; + + memset(array, 0, sizeof(*array)); + + if (capacity > 0) { + if (SZ_MUL_OV(size, capacity, &total)) + return SQFS_ERROR_OVERFLOW; + + array->data = malloc(total); + if (array->data == NULL) + return SQFS_ERROR_ALLOC; + } + + array->size = size; + array->count = capacity; + return 0; +} + +int array_init_copy(array_t *array, const array_t *src) +{ + int ret; + + ret = array_init(array, src->size, src->used); + if (ret != 0) + return ret; + + memcpy(array->data, src->data, src->used * src->size); + array->used = src->used; + return 0; +} + +void array_cleanup(array_t *array) +{ + free(array->data); + memset(array, 0, sizeof(*array)); +} + +int array_append(array_t *array, const void *data) +{ + size_t new_sz, new_count; + void *new; + + if (array->used == array->count) { + if (array->count == 0) { + new_count = 128; + } else { + if (SZ_MUL_OV(array->count, 2, &new_count)) + return SQFS_ERROR_ALLOC; + } + + if (SZ_MUL_OV(new_count, array->size, &new_sz)) + return SQFS_ERROR_ALLOC; + + new = realloc(array->data, new_sz); + if (new == NULL) + return SQFS_ERROR_ALLOC; + + array->data = new; + array->count = new_count; + } + + memcpy((char *)array->data + array->size * array->used, + data, array->size); + + array->used += 1; + return 0; +} + +int array_set_capacity(array_t *array, size_t capacity) +{ + size_t new_sz, new_count; + void *new; + + if (capacity <= array->count) + return 0; + + if (array->count == 0) { + new_count = 128; + } else { + if (SZ_MUL_OV(array->count, 2, &new_count)) + return SQFS_ERROR_ALLOC; + } + + while (new_count < capacity) { + if (SZ_MUL_OV(new_count, 2, &new_count)) + return SQFS_ERROR_ALLOC; + } + + if (SZ_MUL_OV(new_count, array->size, &new_sz)) + return SQFS_ERROR_ALLOC; + + new = realloc(array->data, new_sz); + if (new == NULL) + return SQFS_ERROR_ALLOC; + + array->data = new; + array->count = new_count; + return 0; +} diff --git a/lib/util/src/base64_decode.c b/lib/util/src/base64_decode.c new file mode 100644 index 0000000..b1cf5b6 --- /dev/null +++ b/lib/util/src/base64_decode.c @@ -0,0 +1,103 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * base64_decode.c + * + * Copyright (C) 2022 David Oberhollenzer + */ +#include "config.h" +#include "util/util.h" +#include "util/test.h" + +#include + +static int base64_digit(int c) +{ + if (isupper(c)) + return c - 'A'; + if (islower(c)) + return c - 'a' + 26; + if (isdigit(c)) + return c - '0' + 52; + if (c == '+') + return 62; + if (c == '/' || c == '-') + return 63; + return -1; +} + +int base64_decode(const char *in, size_t in_len, sqfs_u8 *out, size_t *out_len) +{ + int i1, i2, i3, i4; + size_t count = 0; + + while (in_len >= 4) { + i1 = base64_digit(*(in++)); + i2 = base64_digit(*(in++)); + i3 = *(in++); + i4 = *(in++); + in_len -= 4; + + if (i1 < 0 || i2 < 0 || count >= *out_len) + goto fail; + + out[count++] = (i1 << 2) | (i2 >> 4); + + if (i3 == '=' || i3 == '_') { + if ((i4 != '=' && i4 != '_') || in_len > 0) + goto fail; + break; + } + + i3 = base64_digit(i3); + if (i3 < 0 || count >= *out_len) + goto fail; + + out[count++] = ((i2 & 0x0F) << 4) | (i3 >> 2); + + if (i4 == '=' || i4 == '_') { + if (in_len > 0) + goto fail; + break; + } + + i4 = base64_digit(i4); + if (i4 < 0 || count >= *out_len) + goto fail; + + out[count++] = ((i3 & 0x3) << 6) | i4; + } + + /* libarchive has this bizarre bastardization of truncated base64 */ + if (in_len > 0) { + if (in_len == 1) + goto fail; + + i1 = base64_digit(*(in++)); + i2 = base64_digit(*(in++)); + in_len -= 2; + + if (i1 < 0 || i2 < 0 || count >= *out_len) + goto fail; + + out[count++] = (i1 << 2) | (i2 >> 4); + + if (in_len > 0) { + i3 = *(in++); + --in_len; + + if (i3 != '=' && i3 != '_') { + i3 = base64_digit(i3); + if (i3 < 0 || count >= *out_len) + goto fail; + + out[count++] = ((i2 & 0x0F) << 4) | (i3 >> 2); + } + } + } + + *out_len = count; + return 0; +fail: + *out_len = 0; + return -1; +} diff --git a/lib/util/src/canonicalize_name.c b/lib/util/src/canonicalize_name.c new file mode 100644 index 0000000..534e89e --- /dev/null +++ b/lib/util/src/canonicalize_name.c @@ -0,0 +1,60 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * canonicalize_name.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "config.h" +#include "util/util.h" + +static void normalize_slashes(char *filename) +{ + char *dst = filename, *src = filename; + + while (*src == '/') + ++src; + + while (*src != '\0') { + if (*src == '/') { + while (*src == '/') + ++src; + if (*src == '\0') + break; + *(dst++) = '/'; + } else { + *(dst++) = *(src++); + } + } + + *dst = '\0'; +} + +int canonicalize_name(char *filename) +{ + char *dst = filename, *src = filename; + + normalize_slashes(filename); + + while (*src != '\0') { + if (src[0] == '.') { + if (src[1] == '\0') + break; + if (src[1] == '/') { + src += 2; + continue; + } + if (src[1] == '.' && (src[2] == '/' || src[2] == '\0')) + return -1; + } + + while (*src != '\0' && *src != '/') + *(dst++) = *(src++); + + if (*src == '/') + *(dst++) = *(src++); + } + + *dst = '\0'; + normalize_slashes(filename); + return 0; +} diff --git a/lib/util/src/fast_urem_by_const.h b/lib/util/src/fast_urem_by_const.h new file mode 100644 index 0000000..4fb78d3 --- /dev/null +++ b/lib/util/src/fast_urem_by_const.h @@ -0,0 +1,77 @@ +/* + * Copyright © 2010 Valve Software + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "sqfs/predef.h" + +#include +#include + +/* + * Code for fast 32-bit unsigned remainder, based off of "Faster Remainder by + * Direct Computation: Applications to Compilers and Software Libraries," + * available at https://arxiv.org/pdf/1902.01961.pdf. + * + * util_fast_urem32(n, d, REMAINDER_MAGIC(d)) returns the same thing as + * n % d for any unsigned n and d, however it compiles down to only a few + * multiplications, so it should be faster than plain sqfs_u32 modulo if the + * same divisor is used many times. + */ + +#define REMAINDER_MAGIC(divisor) \ + ((sqfs_u64) ~0ull / (divisor) + 1) + +/* + * Get bits 64-96 of a 32x64-bit multiply. If __int128_t is available, we use + * it, which usually compiles down to one instruction on 64-bit architectures. + * Otherwise on 32-bit architectures we usually get four instructions (one + * 32x32->64 multiply, one 32x32->32 multiply, and one 64-bit add). + */ + +static inline sqfs_u32 +_mul32by64_hi(sqfs_u32 a, sqfs_u64 b) +{ +#if defined(__SIZEOF_INT128__) && __SIZEOF_INT128__ == 16 + return ((__uint128_t) b * a) >> 64; +#else + /* + * Let b = b0 + 2^32 * b1. Then a * b = a * b0 + 2^32 * a * b1. We would + * have to do a 96-bit addition to get the full result, except that only + * one term has non-zero lower 32 bits, which means that to get the high 32 + * bits, we only have to add the high 64 bits of each term. Unfortunately, + * we have to do the 64-bit addition in case the low 32 bits overflow. + */ + sqfs_u32 b0 = (sqfs_u32) b; + sqfs_u32 b1 = b >> 32; + return ((((sqfs_u64) a * b0) >> 32) + (sqfs_u64) a * b1) >> 32; +#endif +} + +static inline sqfs_u32 +util_fast_urem32(sqfs_u32 n, sqfs_u32 d, sqfs_u64 magic) +{ + sqfs_u64 lowbits = magic * n; + sqfs_u32 result = _mul32by64_hi(d, lowbits); + assert(result == n % d); + return result; +} + diff --git a/lib/util/src/file_cmp.c b/lib/util/src/file_cmp.c new file mode 100644 index 0000000..2aa0cc2 --- /dev/null +++ b/lib/util/src/file_cmp.c @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * file_cmp.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "config.h" + +#include "util/util.h" +#include "sqfs/io.h" + +#include + +int check_file_range_equal(sqfs_file_t *file, void *scratch, size_t scratch_sz, + sqfs_u64 loc_a, sqfs_u64 loc_b, sqfs_u64 size) +{ + sqfs_u8 *ptr_a = scratch, *ptr_b = ptr_a + scratch_sz / 2; + int ret; + + while (size > 0) { + size_t diff = scratch_sz / 2; + diff = (sqfs_u64)diff > size ? size : diff; + + ret = file->read_at(file, loc_a, ptr_a, diff); + if (ret != 0) + return ret; + + ret = file->read_at(file, loc_b, ptr_b, diff); + if (ret != 0) + return ret; + + if (memcmp(ptr_a, ptr_b, diff) != 0) + return 1; + + size -= diff; + loc_a += diff; + loc_b += diff; + } + + return 0; +} diff --git a/lib/util/src/filename_sane.c b/lib/util/src/filename_sane.c new file mode 100644 index 0000000..b52ce4d --- /dev/null +++ b/lib/util/src/filename_sane.c @@ -0,0 +1,78 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * filename_sane.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "config.h" +#include "util/util.h" + +#include + +#if defined(_WIN32) || defined(__WINDOWS__) || defined(TEST_WIN32) +#ifdef _MSC_VER +#define strncasecmp _strnicmp +#define strcasecmp _stricmp +#endif + +static const char *bad_names[] = { + "CON", "PRN", "AUX", "NUL", + "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9", + "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9", +}; + +static bool is_allowed_by_os(const char *name) +{ + size_t len, i; + + for (i = 0; i < sizeof(bad_names) / sizeof(bad_names[0]); ++i) { + len = strlen(bad_names[i]); + + if (strncasecmp(name, bad_names[i], len) != 0) + continue; + + if (name[len] == '\0') + return false; + + if (name[len] == '.' && strchr(name + len + 1, '.') == NULL) + return false; + } + + return true; +} +#else +static bool is_allowed_by_os(const char *name) +{ + (void)name; + return true; +} +#endif + +bool is_filename_sane(const char *name, bool check_os_specific) +{ + if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0) + return false; + + if (check_os_specific && !is_allowed_by_os(name)) + return false; + + while (*name != '\0') { + if (*name == '/') + return false; + +#if defined(_WIN32) || defined(__WINDOWS__) || defined(TEST_WIN32) + if (check_os_specific) { + if (*name == '<' || *name == '>' || *name == ':') + return false; + if (*name == '"' || *name == '|' || *name == '?') + return false; + if (*name == '*' || *name == '\\' || *name <= 31) + return false; + } +#endif + + ++name; + } + + return true; +} diff --git a/lib/util/src/hash_table.c b/lib/util/src/hash_table.c new file mode 100644 index 0000000..0010e9f --- /dev/null +++ b/lib/util/src/hash_table.c @@ -0,0 +1,417 @@ +/* + * Copyright © 2009,2012 Intel Corporation + * Copyright © 1988-2004 Keith Packard and Bart Massey. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Except as contained in this notice, the names of the authors + * or their institutions shall not be used in advertising or + * otherwise to promote the sale, use or other dealings in this + * Software without prior written authorization from the + * authors. + * + * Authors: + * Eric Anholt + * Keith Packard + */ + +/** + * Implements an open-addressing, linear-reprobing hash table. + * + * For more information, see: + * + * http://cgit.freedesktop.org/~anholt/hash_table/tree/README + */ + +#include +#include +#include + +#include "fast_urem_by_const.h" +#include "util/hash_table.h" + +# define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) + +static const sqfs_u32 deleted_key_value; + +/** + * From Knuth -- a good choice for hash/rehash values is p, p-2 where + * p and p-2 are both prime. These tables are sized to have an extra 10% + * free to avoid exponential performance degradation as the hash table fills + */ +static const struct { + sqfs_u32 max_entries, size, rehash; + sqfs_u64 size_magic, rehash_magic; +} hash_sizes[] = { +#define ENTRY(max_entries, size, rehash) \ + { max_entries, size, rehash, \ + REMAINDER_MAGIC(size), REMAINDER_MAGIC(rehash) } + + ENTRY(2, 5, 3 ), + ENTRY(4, 7, 5 ), + ENTRY(8, 13, 11 ), + ENTRY(16, 19, 17 ), + ENTRY(32, 43, 41 ), + ENTRY(64, 73, 71 ), + ENTRY(128, 151, 149 ), + ENTRY(256, 283, 281 ), + ENTRY(512, 571, 569 ), + ENTRY(1024, 1153, 1151 ), + ENTRY(2048, 2269, 2267 ), + ENTRY(4096, 4519, 4517 ), + ENTRY(8192, 9013, 9011 ), + ENTRY(16384, 18043, 18041 ), + ENTRY(32768, 36109, 36107 ), + ENTRY(65536, 72091, 72089 ), + ENTRY(131072, 144409, 144407 ), + ENTRY(262144, 288361, 288359 ), + ENTRY(524288, 576883, 576881 ), + ENTRY(1048576, 1153459, 1153457 ), + ENTRY(2097152, 2307163, 2307161 ), + ENTRY(4194304, 4613893, 4613891 ), + ENTRY(8388608, 9227641, 9227639 ), + ENTRY(16777216, 18455029, 18455027 ), + ENTRY(33554432, 36911011, 36911009 ), + ENTRY(67108864, 73819861, 73819859 ), + ENTRY(134217728, 147639589, 147639587 ), + ENTRY(268435456, 295279081, 295279079 ), + ENTRY(536870912, 590559793, 590559791 ), + ENTRY(1073741824, 1181116273, 1181116271 ), + ENTRY(2147483648ul, 2362232233ul, 2362232231ul ) +}; + +static inline bool +key_pointer_is_reserved(const struct hash_table *ht, const void *key) +{ + return key == NULL || key == ht->deleted_key; +} + +static int +entry_is_free(const struct hash_entry *entry) +{ + return entry->key == NULL; +} + +static int +entry_is_deleted(const struct hash_table *ht, struct hash_entry *entry) +{ + return entry->key == ht->deleted_key; +} + +static int +entry_is_present(const struct hash_table *ht, struct hash_entry *entry) +{ + return entry->key != NULL && entry->key != ht->deleted_key; +} + +static bool +hash_table_init(struct hash_table *ht, + sqfs_u32 (*key_hash_function)(void *user, const void *key), + bool (*key_equals_function)(void *user, const void *a, + const void *b)) +{ + ht->size_index = 0; + ht->size = hash_sizes[ht->size_index].size; + ht->rehash = hash_sizes[ht->size_index].rehash; + ht->size_magic = hash_sizes[ht->size_index].size_magic; + ht->rehash_magic = hash_sizes[ht->size_index].rehash_magic; + ht->max_entries = hash_sizes[ht->size_index].max_entries; + ht->key_hash_function = key_hash_function; + ht->key_equals_function = key_equals_function; + ht->table = calloc(sizeof(struct hash_entry), ht->size); + ht->entries = 0; + ht->deleted_entries = 0; + ht->deleted_key = &deleted_key_value; + + return ht->table != NULL; +} + +struct hash_table * +hash_table_create(sqfs_u32 (*key_hash_function)(void *user, const void *key), + bool (*key_equals_function)(void *user, const void *a, + const void *b)) +{ + struct hash_table *ht; + + ht = malloc(sizeof(struct hash_table)); + if (ht == NULL) + return NULL; + + if (!hash_table_init(ht, key_hash_function, key_equals_function)) { + free(ht); + return NULL; + } + + return ht; +} + +struct hash_table * +hash_table_clone(struct hash_table *src) +{ + struct hash_table *ht; + + ht = malloc(sizeof(struct hash_table)); + if (ht == NULL) + return NULL; + + memcpy(ht, src, sizeof(struct hash_table)); + + ht->table = calloc(sizeof(struct hash_entry), ht->size); + if (ht->table == NULL) { + free(ht); + return NULL; + } + + memcpy(ht->table, src->table, ht->size * sizeof(struct hash_entry)); + + return ht; +} + +/** + * Frees the given hash table. + */ +void +hash_table_destroy(struct hash_table *ht, + void (*delete_function)(struct hash_entry *entry)) +{ + if (!ht) + return; + + if (delete_function) { + hash_table_foreach(ht, entry) { + delete_function(entry); + } + } + free(ht->table); + free(ht); +} + +static struct hash_entry * +hash_table_search(struct hash_table *ht, sqfs_u32 hash, const void *key) +{ + assert(!key_pointer_is_reserved(ht, key)); + + sqfs_u32 size = ht->size; + sqfs_u32 start_hash_address = util_fast_urem32(hash, size, ht->size_magic); + sqfs_u32 double_hash = 1 + util_fast_urem32(hash, ht->rehash, + ht->rehash_magic); + sqfs_u32 hash_address = start_hash_address; + + do { + struct hash_entry *entry = ht->table + hash_address; + + if (entry_is_free(entry)) { + return NULL; + } else if (entry_is_present(ht, entry) && entry->hash == hash) { + if (ht->key_equals_function(ht->user, key, entry->key)) { + return entry; + } + } + + hash_address += double_hash; + if (hash_address >= size) + hash_address -= size; + } while (hash_address != start_hash_address); + + return NULL; +} + +/** + * Finds a hash table entry with the given key and hash of that key. + * + * Returns NULL if no entry is found. Note that the data pointer may be + * modified by the user. + */ +struct hash_entry * +hash_table_search_pre_hashed(struct hash_table *ht, sqfs_u32 hash, + const void *key) +{ + assert(ht->key_hash_function == NULL || + hash == ht->key_hash_function(ht->user, key)); + return hash_table_search(ht, hash, key); +} + +static void +hash_table_insert_rehash(struct hash_table *ht, sqfs_u32 hash, + const void *key, void *data) +{ + sqfs_u32 size = ht->size; + sqfs_u32 start_hash_address = util_fast_urem32(hash, size, ht->size_magic); + sqfs_u32 double_hash = 1 + util_fast_urem32(hash, ht->rehash, + ht->rehash_magic); + sqfs_u32 hash_address = start_hash_address; + do { + struct hash_entry *entry = ht->table + hash_address; + + if (entry->key == NULL) { + entry->hash = hash; + entry->key = key; + entry->data = data; + return; + } + + hash_address += double_hash; + if (hash_address >= size) + hash_address -= size; + } while (true); +} + +static void +hash_table_rehash(struct hash_table *ht, unsigned new_size_index) +{ + struct hash_table old_ht; + struct hash_entry *table; + + if (new_size_index >= ARRAY_SIZE(hash_sizes)) + return; + + table = calloc(sizeof(struct hash_entry), hash_sizes[new_size_index].size); + if (table == NULL) + return; + + old_ht = *ht; + + ht->table = table; + ht->size_index = new_size_index; + ht->size = hash_sizes[ht->size_index].size; + ht->rehash = hash_sizes[ht->size_index].rehash; + ht->size_magic = hash_sizes[ht->size_index].size_magic; + ht->rehash_magic = hash_sizes[ht->size_index].rehash_magic; + ht->max_entries = hash_sizes[ht->size_index].max_entries; + ht->entries = 0; + ht->deleted_entries = 0; + + hash_table_foreach(&old_ht, entry) { + hash_table_insert_rehash(ht, entry->hash, entry->key, entry->data); + } + + ht->entries = old_ht.entries; + + free(old_ht.table); +} + +static struct hash_entry * +hash_table_insert(struct hash_table *ht, sqfs_u32 hash, + const void *key, void *data) +{ + struct hash_entry *available_entry = NULL; + + assert(!key_pointer_is_reserved(ht, key)); + + if (ht->entries >= ht->max_entries) { + hash_table_rehash(ht, ht->size_index + 1); + } else if (ht->deleted_entries + ht->entries >= ht->max_entries) { + hash_table_rehash(ht, ht->size_index); + } + + sqfs_u32 size = ht->size; + sqfs_u32 start_hash_address = util_fast_urem32(hash, size, ht->size_magic); + sqfs_u32 double_hash = 1 + util_fast_urem32(hash, ht->rehash, + ht->rehash_magic); + sqfs_u32 hash_address = start_hash_address; + do { + struct hash_entry *entry = ht->table + hash_address; + + if (!entry_is_present(ht, entry)) { + /* Stash the first available entry we find */ + if (available_entry == NULL) + available_entry = entry; + if (entry_is_free(entry)) + break; + } + + /* Implement replacement when another insert happens + * with a matching key. This is a relatively common + * feature of hash tables, with the alternative + * generally being "insert the new value as well, and + * return it first when the key is searched for". + * + * Note that the hash table doesn't have a delete + * callback. If freeing of old data pointers is + * required to avoid memory leaks, perform a search + * before inserting. + */ + if (!entry_is_deleted(ht, entry) && + entry->hash == hash && + ht->key_equals_function(ht->user, key, entry->key)) { + entry->key = key; + entry->data = data; + return entry; + } + + hash_address += double_hash; + if (hash_address >= size) + hash_address -= size; + } while (hash_address != start_hash_address); + + if (available_entry) { + if (entry_is_deleted(ht, available_entry)) + ht->deleted_entries--; + available_entry->hash = hash; + available_entry->key = key; + available_entry->data = data; + ht->entries++; + return available_entry; + } + + /* We could hit here if a required resize failed. An unchecked-malloc + * application could ignore this result. + */ + return NULL; +} + +/** + * Inserts the key with the given hash into the table. + * + * Note that insertion may rearrange the table on a resize or rehash, + * so previously found hash_entries are no longer valid after this function. + */ +struct hash_entry * +hash_table_insert_pre_hashed(struct hash_table *ht, sqfs_u32 hash, + const void *key, void *data) +{ + assert(ht->key_hash_function == NULL || + hash == ht->key_hash_function(ht->user, key)); + return hash_table_insert(ht, hash, key, data); +} + +/** + * This function is an iterator over the hash table. + * + * Pass in NULL for the first entry, as in the start of a for loop. Note that + * an iteration over the table is O(table_size) not O(entries). + */ +struct hash_entry * +hash_table_next_entry(struct hash_table *ht, + struct hash_entry *entry) +{ + if (entry == NULL) + entry = ht->table; + else + entry = entry + 1; + + for (; entry != ht->table + ht->size; entry++) { + if (entry_is_present(ht, entry)) { + return entry; + } + } + + return NULL; +} diff --git a/lib/util/src/hex_decode.c b/lib/util/src/hex_decode.c new file mode 100644 index 0000000..ee4b21c --- /dev/null +++ b/lib/util/src/hex_decode.c @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * hex_decode.h + * + * Copyright (C) 2022 David Oberhollenzer + */ +#include "util/util.h" + +#include + +static sqfs_u8 xdigit(int in) +{ + if (isupper(in)) + return in - 'A' + 10; + if (islower(in)) + return in - 'a' + 10; + return in - '0'; +} + +int hex_decode(const char *in, size_t in_sz, sqfs_u8 *out, size_t out_sz) +{ + while (out_sz > 0 && in_sz >= 2 && + isxdigit(in[0]) && isxdigit(in[1])) { + sqfs_u8 hi = xdigit(*(in++)); + sqfs_u8 lo = xdigit(*(in++)); + + *(out++) = (hi << 4) | lo; + + in_sz -= 2; + --out_sz; + } + + return (in_sz > 0) ? -1 : 0; +} diff --git a/lib/util/src/is_memory_zero.c b/lib/util/src/is_memory_zero.c new file mode 100644 index 0000000..aabd45d --- /dev/null +++ b/lib/util/src/is_memory_zero.c @@ -0,0 +1,54 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * is_memory_zero.c + * + * Copyright (C) 2021 David Oberhollenzer + */ +#include "config.h" +#include "util/util.h" + +#include + +#define U64THRESHOLD (128) + +static bool test_u8(const unsigned char *blob, size_t size) +{ + while (size--) { + if (*(blob++) != 0) + return false; + } + + return true; +} + +bool is_memory_zero(const void *blob, size_t size) +{ + const sqfs_u64 *u64ptr; + size_t diff; + + if (size < U64THRESHOLD) + return test_u8(blob, size); + + diff = (uintptr_t)blob % sizeof(sqfs_u64); + + if (diff != 0) { + diff = sizeof(sqfs_u64) - diff; + + if (!test_u8(blob, diff)) + return false; + + blob = (const char *)blob + diff; + size -= diff; + } + + u64ptr = blob; + + while (size >= sizeof(sqfs_u64)) { + if (*(u64ptr++) != 0) + return false; + + size -= sizeof(sqfs_u64); + } + + return test_u8((const unsigned char *)u64ptr, size); +} diff --git a/lib/util/src/mempool.c b/lib/util/src/mempool.c new file mode 100644 index 0000000..e2ddaf0 --- /dev/null +++ b/lib/util/src/mempool.c @@ -0,0 +1,216 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * mempool.c + * + * Copyright (C) 2021 David Oberhollenzer + */ +#include "util/mempool.h" + +#include +#include +#include +#include + +#if defined(_WIN32) || defined(__WINDOWS__) +#define WIN32_LEAN_AND_MEAN +#include +#else +#include +#endif + +#define DEF_POOL_SIZE (65536) +#define MEM_ALIGN (8) + +typedef struct pool_t { + struct pool_t *next; + + unsigned char *data; + unsigned char *limit; + + unsigned int *bitmap; + + size_t obj_free; + + unsigned int blob[]; +} pool_t; + +struct mem_pool_t { + size_t obj_size; + size_t pool_size; + size_t bitmap_count; + pool_t *pool_list; +}; + +static size_t pool_size_from_bitmap_count(size_t count, size_t obj_size) +{ + size_t size, byte_count, bit_count; + + size = sizeof(pool_t); + if (size % sizeof(unsigned int)) + size += sizeof(unsigned int) - size % sizeof(unsigned int); + + byte_count = count * sizeof(unsigned int); + bit_count = byte_count * CHAR_BIT; + + size += byte_count; + if (size % obj_size) + size += obj_size - size % obj_size; + + size += bit_count * obj_size; + return size; +} + +static pool_t *create_pool(const mem_pool_t *mem) +{ + unsigned char *ptr; + pool_t *pool; + +#if defined(_WIN32) || defined(__WINDOWS__) + pool = VirtualAlloc(NULL, mem->pool_size, MEM_RESERVE | MEM_COMMIT, + PAGE_READWRITE); + + if (pool == NULL) + return NULL; +#else + pool = mmap(NULL, mem->pool_size, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + + if (pool == MAP_FAILED) + return NULL; +#endif + pool->bitmap = pool->blob; + pool->obj_free = mem->bitmap_count * sizeof(unsigned int) * CHAR_BIT; + + ptr = (unsigned char *)(pool->bitmap + mem->bitmap_count); + + if (((uintptr_t)ptr) % mem->obj_size) { + ptr += mem->obj_size; + ptr -= ((uintptr_t)ptr) % mem->obj_size; + } + + pool->data = ptr; + pool->limit = pool->data + pool->obj_free * mem->obj_size - 1; + + memset(pool->bitmap, 0, mem->bitmap_count * sizeof(unsigned int)); + return pool; +} + +mem_pool_t *mem_pool_create(size_t obj_size) +{ + mem_pool_t *mem = calloc(1, sizeof(*mem)); + size_t count = 1, total; + + if (mem == NULL) + return NULL; + + if (obj_size % MEM_ALIGN) + obj_size += MEM_ALIGN - obj_size % MEM_ALIGN; + + for (;;) { + total = pool_size_from_bitmap_count(count, obj_size); + if (total > DEF_POOL_SIZE) + break; + ++count; + } + + --count; + + mem->obj_size = obj_size; + mem->pool_size = DEF_POOL_SIZE; + mem->bitmap_count = count; + return mem; +} + +void mem_pool_destroy(mem_pool_t *mem) +{ + while (mem->pool_list != NULL) { + pool_t *pool = mem->pool_list; + mem->pool_list = pool->next; + +#if defined(_WIN32) || defined(__WINDOWS__) + VirtualFree(pool, mem->pool_size, MEM_RELEASE); +#else + munmap(pool, mem->pool_size); +#endif + } + + free(mem); +} + +void *mem_pool_allocate(mem_pool_t *mem) +{ + size_t idx, i, j; + void *ptr = NULL; + pool_t *it; +retry_pool: + for (it = mem->pool_list; it != NULL; it = it->next) { + if (it->obj_free > 0) + break; + } + + if (it == NULL) { + it = create_pool(mem); + if (it == NULL) + return NULL; + + it->next = mem->pool_list; + mem->pool_list = it; + } + + for (i = 0; i < mem->bitmap_count; ++i) { + if (it->bitmap[i] < UINT_MAX) + break; + } + + if (i == mem->bitmap_count) { + it->obj_free = 0; + goto retry_pool; + } + + for (j = 0; j < (sizeof(it->bitmap[i]) * CHAR_BIT); ++j) { + if (!(it->bitmap[i] & (1UL << j))) + break; + } + + if (j == (sizeof(it->bitmap[i]) * CHAR_BIT)) { + it->obj_free = 0; + goto retry_pool; + } + + idx = i * sizeof(unsigned int) * CHAR_BIT + j; + ptr = it->data + idx * mem->obj_size; + + it->bitmap[i] |= (1UL << j); + it->obj_free -= 1; + + memset(ptr, 0, mem->obj_size); + return ptr; +} + +void mem_pool_free(mem_pool_t *mem, void *ptr) +{ + size_t idx, i, j; + pool_t *it; + + for (it = mem->pool_list; it != NULL; it = it->next) { + if ((unsigned char *)ptr >= it->data && + (unsigned char *)ptr < it->limit) { + break; + } + } + + assert(it != NULL); + + idx = (size_t)((unsigned char *)ptr - it->data); + + assert((idx % mem->obj_size) == 0); + idx /= mem->obj_size; + + i = idx / (sizeof(unsigned int) * CHAR_BIT); + j = idx % (sizeof(unsigned int) * CHAR_BIT); + + assert((it->bitmap[i] & (1 << j)) != 0); + + it->bitmap[i] &= ~(1 << j); + it->obj_free += 1; +} diff --git a/lib/util/src/mkdir_p.c b/lib/util/src/mkdir_p.c new file mode 100644 index 0000000..993d8ec --- /dev/null +++ b/lib/util/src/mkdir_p.c @@ -0,0 +1,170 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * mkdir_p.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "util/util.h" + +#include +#include +#include +#include + +#ifdef _WIN32 +/* + Supported paths: + - :\ + - \\\\ + - \\?\:\ + - \\?\UNC\\\ + - Relative path not starting with '\' + */ + +static WCHAR *skip_unc_path(WCHAR *ptr) +{ + /* server */ + if (*ptr == '\0' || *ptr == '\\') + return NULL; + + while (*ptr != '\0' && *ptr != '\\') + ++ptr; + + if (*(ptr++) != '\\') + return NULL; + + /* share */ + if (*ptr == '\0' || *ptr == '\\') + return NULL; + + while (*ptr != '\0' && *ptr != '\\') + ++ptr; + + return (*ptr == '\\') ? (ptr + 1) : ptr; +} + +static WCHAR *skip_prefix(WCHAR *ptr) +{ + if (isalpha(ptr[0]) && ptr[1] == ':' && ptr[2] == '\\') + return ptr + 3; + + if (ptr[0] == '\\' && ptr[1] == '\\') { + if (ptr[2] == '?') { + if (ptr[3] != '\\') + return NULL; + + ptr += 4; + + if ((ptr[0] == 'u' || ptr[0] == 'U') && + (ptr[1] == 'n' || ptr[1] == 'N') && + (ptr[2] == 'c' || ptr[2] == 'C') && + ptr[3] == '\\') { + ptr += 4; + + return skip_unc_path(ptr); + } + + if (isalpha(ptr[0]) && ptr[1] == ':' && ptr[2] == '\\') + return ptr + 3; + + return NULL; + } + + return skip_unc_path(ptr); + } + + if (ptr[0] == '\\') + return NULL; + + return ptr; +} + +int mkdir_p(const char *path) +{ + WCHAR *wpath, *ptr, *end; + DWORD error; + bool done; + + + wpath = path_to_windows(path); + if (wpath == NULL) + return -1; + + ptr = skip_prefix(wpath); + if (ptr == NULL) { + fprintf(stderr, "Illegal or unsupported path: %s\n", path); + goto fail; + } + + while (*ptr != '\0') { + if (*ptr == '\\') { + ++ptr; + continue; + } + + for (end = ptr; *end != '\0' && *end != '\\'; ++end) + ++end; + + if (*end == '\\') { + done = false; + *end = '\0'; + } else { + done = true; + } + + if (!CreateDirectoryW(wpath, NULL)) { + error = GetLastError(); + + if (error != ERROR_ALREADY_EXISTS) { + fprintf(stderr, "Creating %s: %ld\n", + path, error); + goto fail; + } + } + + if (!done) + *end = '\\'; + + ptr = done ? end : (end + 1); + } + + free(wpath); + return 0; +fail: + free(wpath); + return -1; +} +#else +int mkdir_p(const char *path) +{ + size_t i, len; + char *buffer; + + while (path[0] == '/' && path[1] == '/') + ++path; + + if (*path == '\0' || (path[0] == '/' && path[1] == '\0')) + return 0; + + len = strlen(path) + 1; + buffer = alloca(len); + + for (i = 0; i < len; ++i) { + if (i > 0 && (path[i] == '/' || path[i] == '\0')) { + buffer[i] = '\0'; + + if (mkdir(buffer, 0755) != 0) { + if (errno != EEXIST) { + fprintf(stderr, "mkdir %s: %s\n", + buffer, strerror(errno)); + return -1; + } + } + } + + buffer[i] = path[i]; + } + + return 0; +} +#endif diff --git a/lib/util/src/rbtree.c b/lib/util/src/rbtree.c new file mode 100644 index 0000000..8b43e43 --- /dev/null +++ b/lib/util/src/rbtree.c @@ -0,0 +1,267 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * rbtree.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "config.h" + +#include "sqfs/error.h" +#include "util/rbtree.h" + +#include +#include + +#define IS_RED(n) ((n) && (n)->is_red) + +#ifdef NO_CUSTOM_ALLOC +static void destroy_nodes_dfs(rbtree_node_t *n) +{ + rbtree_node_t *l, *r; + + if (n != NULL) { + l = n->left; + r = n->right; + free(n); + destroy_nodes_dfs(l); + destroy_nodes_dfs(r); + } +} +#else +static void destroy_nodes_dfs(rbtree_node_t *n) +{ + (void)n; +} +#endif + +static void flip_colors(rbtree_node_t *n) +{ + n->is_red = !n->is_red; + n->left->is_red = !n->left->is_red; + n->right->is_red = !n->right->is_red; +} + +static rbtree_node_t *rotate_right(rbtree_node_t *n) +{ + rbtree_node_t *x; + + x = n->left; + n->left = x->right; + x->right = n; + + x->is_red = x->right->is_red; + x->right->is_red = 1; + return x; +} + +static rbtree_node_t *rotate_left(rbtree_node_t *n) +{ + rbtree_node_t *x; + + x = n->right; + n->right = x->left; + x->left = n; + + x->is_red = x->left->is_red; + x->left->is_red = 1; + return x; +} + +static rbtree_node_t *subtree_balance(rbtree_node_t *n) +{ + if (IS_RED(n->right) && !IS_RED(n->left)) + n = rotate_left(n); + + if (IS_RED(n->left) && IS_RED(n->left->left)) + n = rotate_right(n); + + if (IS_RED(n->left) && IS_RED(n->right)) + flip_colors(n); + + return n; +} + +static rbtree_node_t *subtree_insert(rbtree_t *tree, rbtree_node_t *root, + rbtree_node_t *new) +{ + if (root == NULL) + return new; + + if (tree->key_compare(tree->key_context, new->data, root->data) < 0) { + root->left = subtree_insert(tree, root->left, new); + } else { + root->right = subtree_insert(tree, root->right, new); + } + + return subtree_balance(root); +} + +static rbtree_node_t *mknode(rbtree_t *t, const void *key, const void *value) +{ + rbtree_node_t *node; + +#ifdef NO_CUSTOM_ALLOC + node = calloc(1, sizeof(*node) + t->key_size_padded + t->value_size); +#else + node = mem_pool_allocate(t->pool); +#endif + + if (node == NULL) + return NULL; + + node->value_offset = t->key_size_padded; + node->is_red = 1; + + memcpy(node->data, key, t->key_size); + memcpy(node->data + t->key_size_padded, value, t->value_size); + return node; +} + +static rbtree_node_t *copy_node(rbtree_t *nt, const rbtree_t *t, + const rbtree_node_t *n) +{ + rbtree_node_t *out; + +#ifdef NO_CUSTOM_ALLOC + out = calloc(1, sizeof(*out) + t->key_size_padded + t->value_size); +#else + out = mem_pool_allocate(nt->pool); +#endif + + if (out == NULL) + return NULL; + + memcpy(out, n, sizeof(*n) + t->key_size_padded + t->value_size); + out->left = NULL; + out->right = NULL; + + if (n->left != NULL) { + out->left = copy_node(nt, t, n->left); + + if (out->left == NULL) { + destroy_nodes_dfs(out); + return NULL; + } + } + + if (n->right != NULL) { + out->right = copy_node(nt, t, n->right); + + if (out->right == NULL) { + destroy_nodes_dfs(out); + return NULL; + } + } + + return out; +} + +int rbtree_init(rbtree_t *tree, size_t keysize, size_t valuesize, + int(*key_compare)(const void *, const void *, const void *)) +{ + size_t diff, size; + + memset(tree, 0, sizeof(*tree)); + tree->key_compare = key_compare; + tree->key_size = keysize; + tree->key_size_padded = keysize; + tree->value_size = valuesize; + + /* make sure the value always has pointer alignment */ + diff = keysize % sizeof(void *); + + if (diff != 0) { + diff = sizeof(void *) - diff; + + if (SZ_ADD_OV(tree->key_size_padded, diff, + &tree->key_size_padded)) { + return SQFS_ERROR_OVERFLOW; + } + } + + /* make sure the node can store the offset */ + if (sizeof(size_t) > sizeof(sqfs_u32)) { + if (tree->key_size_padded > 0x0FFFFFFFFUL) + return SQFS_ERROR_OVERFLOW; + } + + /* make sure the nodes fit in memory */ + size = sizeof(rbtree_node_t); + + if (SZ_ADD_OV(size, tree->key_size_padded, &size)) + return SQFS_ERROR_OVERFLOW; + + if (SZ_ADD_OV(size, tree->value_size, &size)) + return SQFS_ERROR_OVERFLOW; + +#ifndef NO_CUSTOM_ALLOC + /* initialize the underlying pool allocator */ + tree->pool = mem_pool_create(size); + if (tree->pool == NULL) + return SQFS_ERROR_ALLOC; +#endif + return 0; +} + +int rbtree_copy(const rbtree_t *tree, rbtree_t *out) +{ + memcpy(out, tree, sizeof(*out)); + out->root = NULL; + +#ifndef NO_CUSTOM_ALLOC + out->pool = mem_pool_create(sizeof(rbtree_node_t) + + tree->key_size_padded + + tree->value_size); + if (out->pool == NULL) + return SQFS_ERROR_ALLOC; +#endif + + if (tree->root != NULL) { + out->root = copy_node(out, tree, tree->root); + + if (out->root == NULL) { + memset(out, 0, sizeof(*out)); + return SQFS_ERROR_ALLOC; + } + } + + return 0; +} + +void rbtree_cleanup(rbtree_t *tree) +{ +#ifdef NO_CUSTOM_ALLOC + destroy_nodes_dfs(tree->root); +#else + mem_pool_destroy(tree->pool); +#endif + memset(tree, 0, sizeof(*tree)); +} + +int rbtree_insert(rbtree_t *tree, const void *key, const void *value) +{ + rbtree_node_t *node = mknode(tree, key, value); + + if (node == NULL) + return SQFS_ERROR_ALLOC; + + tree->root = subtree_insert(tree, tree->root, node); + tree->root->is_red = 0; + return 0; +} + +rbtree_node_t *rbtree_lookup(const rbtree_t *tree, const void *key) +{ + rbtree_node_t *node = tree->root; + int ret; + + while (node != NULL) { + ret = tree->key_compare(tree->key_context, key, node->data); + if (ret == 0) + break; + + node = ret < 0 ? node->left : node->right; + } + + return node; +} diff --git a/lib/util/src/source_date_epoch.c b/lib/util/src/source_date_epoch.c new file mode 100644 index 0000000..26e5530 --- /dev/null +++ b/lib/util/src/source_date_epoch.c @@ -0,0 +1,44 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * source_date_epoch.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "util/util.h" + +#include +#include +#include + +sqfs_u32 get_source_date_epoch(void) +{ + const char *str, *ptr; + sqfs_u32 x, tval = 0; + + str = getenv("SOURCE_DATE_EPOCH"); + + if (str == NULL || *str == '\0') + return 0; + + for (ptr = str; *ptr != '\0'; ++ptr) { + if (!isdigit(*ptr)) + goto fail_nan; + + x = (*ptr) - '0'; + + if (tval > (UINT32_MAX - x) / 10) + goto fail_ov; + + tval = tval * 10 + x; + } + + return tval; +fail_ov: + fprintf(stderr, "WARNING: SOURCE_DATE_EPOCH=%s does not fit into " + "32 bit integer\n", str); + return 0; +fail_nan: + fprintf(stderr, "WARNING: SOURCE_DATE_EPOCH=%s is not a positive " + "number\n", str); + return 0; +} diff --git a/lib/util/src/str_table.c b/lib/util/src/str_table.c new file mode 100644 index 0000000..2d3e354 --- /dev/null +++ b/lib/util/src/str_table.c @@ -0,0 +1,183 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * str_table.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "config.h" + +#include +#include +#include + +#include "sqfs/error.h" +#include "util/str_table.h" +#include "util/util.h" + +/* R5 hash function (borrowed from reiserfs) */ +static sqfs_u32 strhash(const char *s) +{ + const signed char *str = (const signed char *)s; + sqfs_u32 a = 0; + + while (*str != '\0') { + a += *str << 4; + a += *str >> 4; + a *= 11; + str++; + } + + return a; +} + +static bool key_equals_function(void *user, const void *a, const void *b) +{ + (void)user; + return strcmp(a, b) == 0; +} + +int str_table_init(str_table_t *table) +{ + memset(table, 0, sizeof(*table)); + + if (array_init(&table->bucket_ptrs, sizeof(str_bucket_t *), 0)) + goto fail_arr; + + table->ht = hash_table_create(NULL, key_equals_function); + if (table->ht == NULL) + goto fail_ht; + + return 0; +fail_ht: + array_cleanup(&table->bucket_ptrs); +fail_arr: + memset(table, 0, sizeof(*table)); + return SQFS_ERROR_ALLOC; +} + +int str_table_copy(str_table_t *dst, const str_table_t *src) +{ + str_bucket_t *bucket, **array; + int ret; + + ret = array_init_copy(&dst->bucket_ptrs, &src->bucket_ptrs); + if (ret != 0) + return ret; + + dst->ht = hash_table_clone(src->ht); + if (dst->ht == NULL) { + array_cleanup(&dst->bucket_ptrs); + return SQFS_ERROR_ALLOC; + } + + array = (str_bucket_t **)dst->bucket_ptrs.data; + + hash_table_foreach(dst->ht, ent) { + bucket = alloc_flex(sizeof(*bucket), 1, strlen(ent->key) + 1); + if (bucket == NULL) { + str_table_cleanup(dst); + return SQFS_ERROR_ALLOC; + } + + memcpy(bucket, ent->data, + sizeof(*bucket) + strlen(ent->key) + 1); + + ent->data = bucket; + ent->key = bucket->string; + + array[bucket->index] = bucket; + } + + return 0; +} + +void str_table_cleanup(str_table_t *table) +{ + hash_table_foreach(table->ht, ent) { + free(ent->data); + ent->data = NULL; + ent->key = NULL; + } + + hash_table_destroy(table->ht, NULL); + array_cleanup(&table->bucket_ptrs); + memset(table, 0, sizeof(*table)); +} + +int str_table_get_index(str_table_t *table, const char *str, size_t *idx) +{ + struct hash_entry *ent; + str_bucket_t *new; + sqfs_u32 hash; + + hash = strhash(str); + ent = hash_table_search_pre_hashed(table->ht, hash, str); + + if (ent != NULL) { + *idx = ((str_bucket_t *)ent->data)->index; + return 0; + } + + new = alloc_flex(sizeof(*new), 1, strlen(str) + 1); + if (new == NULL) + return SQFS_ERROR_ALLOC; + + new->index = table->next_index; + strcpy(new->string, str); + + ent = hash_table_insert_pre_hashed(table->ht, hash, str, new); + if (ent == NULL) { + free(new); + return SQFS_ERROR_ALLOC; + } + + ent->key = new->string; + + if (array_append(&table->bucket_ptrs, &new) != 0) { + free(new); + ent->key = NULL; + ent->data = NULL; + return SQFS_ERROR_ALLOC; + } + + *idx = table->next_index++; + return 0; +} + +static str_bucket_t *bucket_by_index(const str_table_t *table, size_t index) +{ + if (index >= table->bucket_ptrs.used) + return NULL; + + return ((str_bucket_t **)table->bucket_ptrs.data)[index]; +} + +const char *str_table_get_string(const str_table_t *table, size_t index) +{ + str_bucket_t *bucket = bucket_by_index(table, index); + + return bucket == NULL ? NULL : bucket->string; +} + +void str_table_add_ref(str_table_t *table, size_t index) +{ + str_bucket_t *bucket = bucket_by_index(table, index); + + if (bucket != NULL && bucket->refcount < ~((size_t)0)) + bucket->refcount += 1; +} + +void str_table_del_ref(str_table_t *table, size_t index) +{ + str_bucket_t *bucket = bucket_by_index(table, index); + + if (bucket != NULL && bucket->refcount > 0) + bucket->refcount -= 1; +} + +size_t str_table_get_ref_count(const str_table_t *table, size_t index) +{ + str_bucket_t *bucket = bucket_by_index(table, index); + + return bucket != NULL ? bucket->refcount : 0; +} diff --git a/lib/util/src/threadpool.c b/lib/util/src/threadpool.c new file mode 100644 index 0000000..c7357cd --- /dev/null +++ b/lib/util/src/threadpool.c @@ -0,0 +1,392 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * threadpool.c + * + * Copyright (C) 2021 David Oberhollenzer + */ +#include "util/threadpool.h" +#include "util/util.h" + +#include +#include + +#if defined(_WIN32) || defined(__WINDOWS__) +#include "util/w32threadwrap.h" + +#define THREAD_FUN(funname, argname) DWORD WINAPI funname(LPVOID argname) +#define THREAD_EXIT_SUCCESS (0) +#else +#include +#include + +#define THREAD_FUN(funname, argname) void *funname(void *argname) +#define THREAD_EXIT_SUCCESS NULL +#endif + +typedef struct thread_pool_impl_t thread_pool_impl_t; + +typedef struct work_item_t { + struct work_item_t *next; + size_t ticket_number; + + void *data; +} work_item_t; + +typedef struct { + pthread_t thread; + thread_pool_impl_t *pool; + + thread_pool_worker_t fun; + void *user; +} worker_t; + +struct thread_pool_impl_t { + thread_pool_t base; + + pthread_mutex_t mtx; + pthread_cond_t queue_cond; + pthread_cond_t done_cond; + + size_t next_ticket; + size_t next_dequeue_ticket; + + size_t item_count; + + work_item_t *queue; + work_item_t *queue_last; + + work_item_t *done; + + work_item_t *safe_done; + work_item_t *safe_done_last; + + work_item_t *recycle; + + int status; + + size_t num_workers; + worker_t workers[]; +}; + +/*****************************************************************************/ + +static void store_completed(thread_pool_impl_t *pool, work_item_t *done, + int status) +{ + work_item_t *it = pool->done, *prev = NULL; + + while (it != NULL) { + if (it->ticket_number >= done->ticket_number) + break; + + prev = it; + it = it->next; + } + + if (prev == NULL) { + done->next = pool->done; + pool->done = done; + } else { + done->next = prev->next; + prev->next = done; + } + + if (status != 0 && pool->status == 0) + pool->status = status; + + pthread_cond_broadcast(&pool->done_cond); +} + +static work_item_t *get_next_work_item(thread_pool_impl_t *pool) +{ + work_item_t *item = NULL; + + while (pool->queue == NULL && pool->status == 0) + pthread_cond_wait(&pool->queue_cond, &pool->mtx); + + if (pool->status == 0) { + item = pool->queue; + pool->queue = item->next; + item->next = NULL; + + if (pool->queue == NULL) + pool->queue_last = NULL; + } + + return item; +} + +static THREAD_FUN(worker_proc, arg) +{ + work_item_t *item = NULL; + worker_t *worker = arg; + int status = 0; + + for (;;) { + pthread_mutex_lock(&worker->pool->mtx); + if (item != NULL) + store_completed(worker->pool, item, status); + + item = get_next_work_item(worker->pool); + pthread_mutex_unlock(&worker->pool->mtx); + + if (item == NULL) + break; + + status = worker->fun(worker->user, item->data); + } + + return THREAD_EXIT_SUCCESS; +} + +/*****************************************************************************/ + +static work_item_t *try_dequeue_done(thread_pool_impl_t *pool) +{ + work_item_t *out; + + if (pool->done == NULL) + return NULL; + + if (pool->done->ticket_number != pool->next_dequeue_ticket) + return NULL; + + out = pool->done; + pool->done = out->next; + out->next = NULL; + pool->next_dequeue_ticket += 1; + return out; +} + +static void free_item_list(work_item_t *list) +{ + while (list != NULL) { + work_item_t *item = list; + list = list->next; + free(item); + } +} + +static void destroy(thread_pool_t *interface) +{ + thread_pool_impl_t *pool = (thread_pool_impl_t *)interface; + size_t i; + + pthread_mutex_lock(&pool->mtx); + pool->status = -1; + pthread_cond_broadcast(&pool->queue_cond); + pthread_mutex_unlock(&pool->mtx); + + for (i = 0; i < pool->num_workers; ++i) + pthread_join(pool->workers[i].thread, NULL); + + pthread_cond_destroy(&pool->done_cond); + pthread_cond_destroy(&pool->queue_cond); + pthread_mutex_destroy(&pool->mtx); + + free_item_list(pool->queue); + free_item_list(pool->done); + free_item_list(pool->recycle); + free_item_list(pool->safe_done); + free(pool); +} + +static size_t get_worker_count(thread_pool_t *interface) +{ + thread_pool_impl_t *pool = (thread_pool_impl_t *)interface; + + return pool->num_workers; +} + +static void set_worker_ptr(thread_pool_t *interface, size_t idx, void *ptr) +{ + thread_pool_impl_t *pool = (thread_pool_impl_t *)interface; + + if (idx >= pool->num_workers) + return; + + pthread_mutex_lock(&pool->mtx); + pool->workers[idx].user = ptr; + pthread_mutex_unlock(&pool->mtx); +} + +static int submit(thread_pool_t *interface, void *ptr) +{ + thread_pool_impl_t *pool = (thread_pool_impl_t *)interface; + work_item_t *item = NULL; + int status; + + if (pool->recycle != NULL) { + item = pool->recycle; + pool->recycle = item->next; + item->next = NULL; + } + + if (item == NULL) { + item = calloc(1, sizeof(*item)); + if (item == NULL) + return -1; + } + + pthread_mutex_lock(&pool->mtx); + status = pool->status; + + if (status == 0) { + item->data = ptr; + item->ticket_number = pool->next_ticket++; + + if (pool->queue_last == NULL) { + pool->queue = item; + } else { + pool->queue_last->next = item; + } + + pool->queue_last = item; + pool->item_count += 1; + } + + for (;;) { + work_item_t *done = try_dequeue_done(pool); + if (done == NULL) + break; + + if (pool->safe_done_last == NULL) { + pool->safe_done = done; + } else { + pool->safe_done_last->next = done; + } + + pool->safe_done_last = done; + } + + pthread_cond_broadcast(&pool->queue_cond); + pthread_mutex_unlock(&pool->mtx); + + if (status != 0) { + memset(item, 0, sizeof(*item)); + item->next = pool->recycle; + pool->recycle = item; + } + + return status; +} + +static void *dequeue(thread_pool_t *interface) +{ + thread_pool_impl_t *pool = (thread_pool_impl_t *)interface; + work_item_t *out = NULL; + void *ptr = NULL; + + if (pool->item_count == 0) + return NULL; + + if (pool->safe_done != NULL) { + out = pool->safe_done; + + pool->safe_done = pool->safe_done->next; + if (pool->safe_done == NULL) + pool->safe_done_last = NULL; + } else { + pthread_mutex_lock(&pool->mtx); + for (;;) { + out = try_dequeue_done(pool); + if (out != NULL) + break; + + pthread_cond_wait(&pool->done_cond, &pool->mtx); + } + pthread_mutex_unlock(&pool->mtx); + } + + ptr = out->data; + + out->ticket_number = 0; + out->data = NULL; + out->next = pool->recycle; + pool->recycle = out; + + pool->item_count -= 1; + return ptr; +} + +static int get_status(thread_pool_t *interface) +{ + thread_pool_impl_t *pool = (thread_pool_impl_t *)interface; + int status; + + pthread_mutex_lock(&pool->mtx); + status = pool->status; + pthread_mutex_unlock(&pool->mtx); + + return status; +} + +thread_pool_t *thread_pool_create(size_t num_jobs, thread_pool_worker_t worker) +{ + thread_pool_impl_t *pool; + thread_pool_t *interface; + sigset_t set, oldset; + size_t i, j; + int ret; + + if (num_jobs < 1) + num_jobs = 1; + + pool = alloc_flex(sizeof(*pool), sizeof(pool->workers[0]), num_jobs); + if (pool == NULL) + return NULL; + + if (pthread_mutex_init(&pool->mtx, NULL) != 0) + goto fail_free; + + if (pthread_cond_init(&pool->queue_cond, NULL) != 0) + goto fail_mtx; + + if (pthread_cond_init(&pool->done_cond, NULL) != 0) + goto fail_qcond; + + sigfillset(&set); + pthread_sigmask(SIG_SETMASK, &set, &oldset); + + pool->num_workers = num_jobs; + + for (i = 0; i < num_jobs; ++i) { + pool->workers[i].fun = worker; + pool->workers[i].pool = pool; + + ret = pthread_create(&pool->workers[i].thread, NULL, + worker_proc, pool->workers + i); + + if (ret != 0) + goto fail; + } + + pthread_sigmask(SIG_SETMASK, &oldset, NULL); + + interface = (thread_pool_t *)pool; + interface->destroy = destroy; + interface->get_worker_count = get_worker_count; + interface->set_worker_ptr = set_worker_ptr; + interface->submit = submit; + interface->dequeue = dequeue; + interface->get_status = get_status; + return interface; +fail: + pthread_mutex_lock(&pool->mtx); + pool->status = -1; + pthread_cond_broadcast(&pool->queue_cond); + pthread_mutex_unlock(&pool->mtx); + + for (j = 0; j < i; ++j) + pthread_join(pool->workers[j].thread, NULL); + + pthread_sigmask(SIG_SETMASK, &oldset, NULL); + pthread_cond_destroy(&pool->done_cond); +fail_qcond: + pthread_cond_destroy(&pool->queue_cond); +fail_mtx: + pthread_mutex_destroy(&pool->mtx); +fail_free: + free(pool); + return NULL; +} diff --git a/lib/util/src/threadpool_serial.c b/lib/util/src/threadpool_serial.c new file mode 100644 index 0000000..fb24ee8 --- /dev/null +++ b/lib/util/src/threadpool_serial.c @@ -0,0 +1,162 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * threadpool_serial.c + * + * Copyright (C) 2021 David Oberhollenzer + */ +#include "util/threadpool.h" +#include "util/util.h" + +#include +#include + +typedef struct work_item_t { + struct work_item_t *next; + void *data; +} work_item_t; + +typedef struct { + thread_pool_t base; + + work_item_t *queue; + work_item_t *queue_last; + + work_item_t *recycle; + + thread_pool_worker_t fun; + void *user; + int status; +} serial_impl_t; + +static void destroy(thread_pool_t *interface) +{ + serial_impl_t *pool = (serial_impl_t *)interface; + + while (pool->queue != NULL) { + work_item_t *item = pool->queue; + pool->queue = item->next; + free(item); + } + + while (pool->recycle != NULL) { + work_item_t *item = pool->recycle; + pool->recycle = item->next; + free(item); + } + + free(pool); +} + +static size_t get_worker_count(thread_pool_t *pool) +{ + (void)pool; + return 1; +} + +static void set_worker_ptr(thread_pool_t *interface, size_t idx, void *ptr) +{ + serial_impl_t *pool = (serial_impl_t *)interface; + + if (idx >= 1) + return; + + pool->user = ptr; +} + +static int submit(thread_pool_t *interface, void *ptr) +{ + serial_impl_t *pool = (serial_impl_t *)interface; + work_item_t *item = NULL; + + if (pool->status != 0) + return pool->status; + + if (pool->recycle != NULL) { + item = pool->recycle; + pool->recycle = item->next; + item->next = NULL; + } + + if (item == NULL) { + item = calloc(1, sizeof(*item)); + if (item == NULL) + return -1; + } + + item->data = ptr; + + if (pool->queue_last == NULL) { + pool->queue = item; + } else { + pool->queue_last->next = item; + } + + pool->queue_last = item; + return 0; +} + +static void *dequeue(thread_pool_t *interface) +{ + serial_impl_t *pool = (serial_impl_t *)interface; + work_item_t *item; + void *ptr; + int ret; + + if (pool->queue == NULL) + return NULL; + + item = pool->queue; + pool->queue = item->next; + + if (pool->queue == NULL) + pool->queue_last = NULL; + + ptr = item->data; + + memset(item, 0, sizeof(*item)); + item->next = pool->recycle; + pool->recycle = item; + + ret = pool->fun(pool->user, ptr); + + if (ret != 0 && pool->status == 0) + pool->status = ret; + + return ptr; +} + +static int get_status(thread_pool_t *interface) +{ + serial_impl_t *pool = (serial_impl_t *)interface; + + return pool->status; +} + +thread_pool_t *thread_pool_create_serial(thread_pool_worker_t worker) +{ + serial_impl_t *pool = calloc(1, sizeof(*pool)); + thread_pool_t *interface = (thread_pool_t *)pool; + + if (pool == NULL) + return NULL; + + pool->fun = worker; + + interface = (thread_pool_t *)pool; + interface->destroy = destroy; + interface->get_worker_count = get_worker_count; + interface->set_worker_ptr = set_worker_ptr; + interface->submit = submit; + interface->dequeue = dequeue; + interface->get_status = get_status; + return interface; + +} + +#ifdef NO_THREAD_IMPL +thread_pool_t *thread_pool_create(size_t num_jobs, thread_pool_worker_t worker) +{ + (void)num_jobs; + return thread_pool_create_serial(worker); +} +#endif diff --git a/lib/util/src/xxhash.c b/lib/util/src/xxhash.c new file mode 100644 index 0000000..60467fb --- /dev/null +++ b/lib/util/src/xxhash.c @@ -0,0 +1,112 @@ +/* + * xxHash - Extremely Fast Hash algorithm + * Copyright (C) 2012-2016, Yann Collet. + * + * BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + * This code has been lifted from Linux (the OS kernel) and adapted for use in + * libsquashfs. For the original, unmodified and complete source, see below. + * + * You can contact the author at: + * - xxHash homepage: http://cyan4973.github.io/xxHash/ + * - xxHash source repository: https://github.com/Cyan4973/xxHash + */ +#include "config.h" +#include "util/util.h" + +#include + +#define xxh_rotl32(x, r) ((x << r) | (x >> (32 - r))) + +static const sqfs_u32 PRIME32_1 = 2654435761U; +static const sqfs_u32 PRIME32_2 = 2246822519U; +static const sqfs_u32 PRIME32_3 = 3266489917U; +static const sqfs_u32 PRIME32_4 = 668265263U; +static const sqfs_u32 PRIME32_5 = 374761393U; + +static sqfs_u32 xxh32_round(sqfs_u32 seed, sqfs_u32 input) +{ + seed += input * PRIME32_2; + seed = xxh_rotl32(seed, 13); + seed *= PRIME32_1; + return seed; +} + +static sqfs_u32 XXH_readLE32(const sqfs_u8 *ptr) +{ + sqfs_u32 value; + memcpy(&value, ptr, sizeof(value)); + return le32toh(value); +} + +sqfs_u32 xxh32(const void *input, const size_t len) +{ + const sqfs_u8 *p = (const sqfs_u8 *)input; + const sqfs_u8 *b_end = p + len; + sqfs_u32 h32; + + if (len >= 16) { + const sqfs_u8 *const limit = b_end - 16; + sqfs_u32 v1 = PRIME32_1 + PRIME32_2; + sqfs_u32 v2 = PRIME32_2; + sqfs_u32 v3 = 0; + sqfs_u32 v4 = PRIME32_1; + + do { + v1 = xxh32_round(v1, XXH_readLE32(p )); + v2 = xxh32_round(v2, XXH_readLE32(p + 4)); + v3 = xxh32_round(v3, XXH_readLE32(p + 8)); + v4 = xxh32_round(v4, XXH_readLE32(p + 12)); + p += 16; + } while (p <= limit); + + h32 = xxh_rotl32(v1, 1) + xxh_rotl32(v2, 7) + + xxh_rotl32(v3, 12) + xxh_rotl32(v4, 18); + } else { + h32 = PRIME32_5; + } + + h32 += (sqfs_u32)len; + + while (p + 4 <= b_end) { + h32 += XXH_readLE32(p) * PRIME32_3; + h32 = xxh_rotl32(h32, 17) * PRIME32_4; + p += 4; + } + + while (p < b_end) { + h32 += (*p) * PRIME32_5; + h32 = xxh_rotl32(h32, 11) * PRIME32_1; + p++; + } + + h32 ^= h32 >> 15; + h32 *= PRIME32_2; + h32 ^= h32 >> 13; + h32 *= PRIME32_3; + h32 ^= h32 >> 16; + return h32; +} diff --git a/lib/util/str_table.c b/lib/util/str_table.c deleted file mode 100644 index 2d3e354..0000000 --- a/lib/util/str_table.c +++ /dev/null @@ -1,183 +0,0 @@ -/* SPDX-License-Identifier: LGPL-3.0-or-later */ -/* - * str_table.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "config.h" - -#include -#include -#include - -#include "sqfs/error.h" -#include "util/str_table.h" -#include "util/util.h" - -/* R5 hash function (borrowed from reiserfs) */ -static sqfs_u32 strhash(const char *s) -{ - const signed char *str = (const signed char *)s; - sqfs_u32 a = 0; - - while (*str != '\0') { - a += *str << 4; - a += *str >> 4; - a *= 11; - str++; - } - - return a; -} - -static bool key_equals_function(void *user, const void *a, const void *b) -{ - (void)user; - return strcmp(a, b) == 0; -} - -int str_table_init(str_table_t *table) -{ - memset(table, 0, sizeof(*table)); - - if (array_init(&table->bucket_ptrs, sizeof(str_bucket_t *), 0)) - goto fail_arr; - - table->ht = hash_table_create(NULL, key_equals_function); - if (table->ht == NULL) - goto fail_ht; - - return 0; -fail_ht: - array_cleanup(&table->bucket_ptrs); -fail_arr: - memset(table, 0, sizeof(*table)); - return SQFS_ERROR_ALLOC; -} - -int str_table_copy(str_table_t *dst, const str_table_t *src) -{ - str_bucket_t *bucket, **array; - int ret; - - ret = array_init_copy(&dst->bucket_ptrs, &src->bucket_ptrs); - if (ret != 0) - return ret; - - dst->ht = hash_table_clone(src->ht); - if (dst->ht == NULL) { - array_cleanup(&dst->bucket_ptrs); - return SQFS_ERROR_ALLOC; - } - - array = (str_bucket_t **)dst->bucket_ptrs.data; - - hash_table_foreach(dst->ht, ent) { - bucket = alloc_flex(sizeof(*bucket), 1, strlen(ent->key) + 1); - if (bucket == NULL) { - str_table_cleanup(dst); - return SQFS_ERROR_ALLOC; - } - - memcpy(bucket, ent->data, - sizeof(*bucket) + strlen(ent->key) + 1); - - ent->data = bucket; - ent->key = bucket->string; - - array[bucket->index] = bucket; - } - - return 0; -} - -void str_table_cleanup(str_table_t *table) -{ - hash_table_foreach(table->ht, ent) { - free(ent->data); - ent->data = NULL; - ent->key = NULL; - } - - hash_table_destroy(table->ht, NULL); - array_cleanup(&table->bucket_ptrs); - memset(table, 0, sizeof(*table)); -} - -int str_table_get_index(str_table_t *table, const char *str, size_t *idx) -{ - struct hash_entry *ent; - str_bucket_t *new; - sqfs_u32 hash; - - hash = strhash(str); - ent = hash_table_search_pre_hashed(table->ht, hash, str); - - if (ent != NULL) { - *idx = ((str_bucket_t *)ent->data)->index; - return 0; - } - - new = alloc_flex(sizeof(*new), 1, strlen(str) + 1); - if (new == NULL) - return SQFS_ERROR_ALLOC; - - new->index = table->next_index; - strcpy(new->string, str); - - ent = hash_table_insert_pre_hashed(table->ht, hash, str, new); - if (ent == NULL) { - free(new); - return SQFS_ERROR_ALLOC; - } - - ent->key = new->string; - - if (array_append(&table->bucket_ptrs, &new) != 0) { - free(new); - ent->key = NULL; - ent->data = NULL; - return SQFS_ERROR_ALLOC; - } - - *idx = table->next_index++; - return 0; -} - -static str_bucket_t *bucket_by_index(const str_table_t *table, size_t index) -{ - if (index >= table->bucket_ptrs.used) - return NULL; - - return ((str_bucket_t **)table->bucket_ptrs.data)[index]; -} - -const char *str_table_get_string(const str_table_t *table, size_t index) -{ - str_bucket_t *bucket = bucket_by_index(table, index); - - return bucket == NULL ? NULL : bucket->string; -} - -void str_table_add_ref(str_table_t *table, size_t index) -{ - str_bucket_t *bucket = bucket_by_index(table, index); - - if (bucket != NULL && bucket->refcount < ~((size_t)0)) - bucket->refcount += 1; -} - -void str_table_del_ref(str_table_t *table, size_t index) -{ - str_bucket_t *bucket = bucket_by_index(table, index); - - if (bucket != NULL && bucket->refcount > 0) - bucket->refcount -= 1; -} - -size_t str_table_get_ref_count(const str_table_t *table, size_t index) -{ - str_bucket_t *bucket = bucket_by_index(table, index); - - return bucket != NULL ? bucket->refcount : 0; -} diff --git a/lib/util/threadpool.c b/lib/util/threadpool.c deleted file mode 100644 index c7357cd..0000000 --- a/lib/util/threadpool.c +++ /dev/null @@ -1,392 +0,0 @@ -/* SPDX-License-Identifier: LGPL-3.0-or-later */ -/* - * threadpool.c - * - * Copyright (C) 2021 David Oberhollenzer - */ -#include "util/threadpool.h" -#include "util/util.h" - -#include -#include - -#if defined(_WIN32) || defined(__WINDOWS__) -#include "util/w32threadwrap.h" - -#define THREAD_FUN(funname, argname) DWORD WINAPI funname(LPVOID argname) -#define THREAD_EXIT_SUCCESS (0) -#else -#include -#include - -#define THREAD_FUN(funname, argname) void *funname(void *argname) -#define THREAD_EXIT_SUCCESS NULL -#endif - -typedef struct thread_pool_impl_t thread_pool_impl_t; - -typedef struct work_item_t { - struct work_item_t *next; - size_t ticket_number; - - void *data; -} work_item_t; - -typedef struct { - pthread_t thread; - thread_pool_impl_t *pool; - - thread_pool_worker_t fun; - void *user; -} worker_t; - -struct thread_pool_impl_t { - thread_pool_t base; - - pthread_mutex_t mtx; - pthread_cond_t queue_cond; - pthread_cond_t done_cond; - - size_t next_ticket; - size_t next_dequeue_ticket; - - size_t item_count; - - work_item_t *queue; - work_item_t *queue_last; - - work_item_t *done; - - work_item_t *safe_done; - work_item_t *safe_done_last; - - work_item_t *recycle; - - int status; - - size_t num_workers; - worker_t workers[]; -}; - -/*****************************************************************************/ - -static void store_completed(thread_pool_impl_t *pool, work_item_t *done, - int status) -{ - work_item_t *it = pool->done, *prev = NULL; - - while (it != NULL) { - if (it->ticket_number >= done->ticket_number) - break; - - prev = it; - it = it->next; - } - - if (prev == NULL) { - done->next = pool->done; - pool->done = done; - } else { - done->next = prev->next; - prev->next = done; - } - - if (status != 0 && pool->status == 0) - pool->status = status; - - pthread_cond_broadcast(&pool->done_cond); -} - -static work_item_t *get_next_work_item(thread_pool_impl_t *pool) -{ - work_item_t *item = NULL; - - while (pool->queue == NULL && pool->status == 0) - pthread_cond_wait(&pool->queue_cond, &pool->mtx); - - if (pool->status == 0) { - item = pool->queue; - pool->queue = item->next; - item->next = NULL; - - if (pool->queue == NULL) - pool->queue_last = NULL; - } - - return item; -} - -static THREAD_FUN(worker_proc, arg) -{ - work_item_t *item = NULL; - worker_t *worker = arg; - int status = 0; - - for (;;) { - pthread_mutex_lock(&worker->pool->mtx); - if (item != NULL) - store_completed(worker->pool, item, status); - - item = get_next_work_item(worker->pool); - pthread_mutex_unlock(&worker->pool->mtx); - - if (item == NULL) - break; - - status = worker->fun(worker->user, item->data); - } - - return THREAD_EXIT_SUCCESS; -} - -/*****************************************************************************/ - -static work_item_t *try_dequeue_done(thread_pool_impl_t *pool) -{ - work_item_t *out; - - if (pool->done == NULL) - return NULL; - - if (pool->done->ticket_number != pool->next_dequeue_ticket) - return NULL; - - out = pool->done; - pool->done = out->next; - out->next = NULL; - pool->next_dequeue_ticket += 1; - return out; -} - -static void free_item_list(work_item_t *list) -{ - while (list != NULL) { - work_item_t *item = list; - list = list->next; - free(item); - } -} - -static void destroy(thread_pool_t *interface) -{ - thread_pool_impl_t *pool = (thread_pool_impl_t *)interface; - size_t i; - - pthread_mutex_lock(&pool->mtx); - pool->status = -1; - pthread_cond_broadcast(&pool->queue_cond); - pthread_mutex_unlock(&pool->mtx); - - for (i = 0; i < pool->num_workers; ++i) - pthread_join(pool->workers[i].thread, NULL); - - pthread_cond_destroy(&pool->done_cond); - pthread_cond_destroy(&pool->queue_cond); - pthread_mutex_destroy(&pool->mtx); - - free_item_list(pool->queue); - free_item_list(pool->done); - free_item_list(pool->recycle); - free_item_list(pool->safe_done); - free(pool); -} - -static size_t get_worker_count(thread_pool_t *interface) -{ - thread_pool_impl_t *pool = (thread_pool_impl_t *)interface; - - return pool->num_workers; -} - -static void set_worker_ptr(thread_pool_t *interface, size_t idx, void *ptr) -{ - thread_pool_impl_t *pool = (thread_pool_impl_t *)interface; - - if (idx >= pool->num_workers) - return; - - pthread_mutex_lock(&pool->mtx); - pool->workers[idx].user = ptr; - pthread_mutex_unlock(&pool->mtx); -} - -static int submit(thread_pool_t *interface, void *ptr) -{ - thread_pool_impl_t *pool = (thread_pool_impl_t *)interface; - work_item_t *item = NULL; - int status; - - if (pool->recycle != NULL) { - item = pool->recycle; - pool->recycle = item->next; - item->next = NULL; - } - - if (item == NULL) { - item = calloc(1, sizeof(*item)); - if (item == NULL) - return -1; - } - - pthread_mutex_lock(&pool->mtx); - status = pool->status; - - if (status == 0) { - item->data = ptr; - item->ticket_number = pool->next_ticket++; - - if (pool->queue_last == NULL) { - pool->queue = item; - } else { - pool->queue_last->next = item; - } - - pool->queue_last = item; - pool->item_count += 1; - } - - for (;;) { - work_item_t *done = try_dequeue_done(pool); - if (done == NULL) - break; - - if (pool->safe_done_last == NULL) { - pool->safe_done = done; - } else { - pool->safe_done_last->next = done; - } - - pool->safe_done_last = done; - } - - pthread_cond_broadcast(&pool->queue_cond); - pthread_mutex_unlock(&pool->mtx); - - if (status != 0) { - memset(item, 0, sizeof(*item)); - item->next = pool->recycle; - pool->recycle = item; - } - - return status; -} - -static void *dequeue(thread_pool_t *interface) -{ - thread_pool_impl_t *pool = (thread_pool_impl_t *)interface; - work_item_t *out = NULL; - void *ptr = NULL; - - if (pool->item_count == 0) - return NULL; - - if (pool->safe_done != NULL) { - out = pool->safe_done; - - pool->safe_done = pool->safe_done->next; - if (pool->safe_done == NULL) - pool->safe_done_last = NULL; - } else { - pthread_mutex_lock(&pool->mtx); - for (;;) { - out = try_dequeue_done(pool); - if (out != NULL) - break; - - pthread_cond_wait(&pool->done_cond, &pool->mtx); - } - pthread_mutex_unlock(&pool->mtx); - } - - ptr = out->data; - - out->ticket_number = 0; - out->data = NULL; - out->next = pool->recycle; - pool->recycle = out; - - pool->item_count -= 1; - return ptr; -} - -static int get_status(thread_pool_t *interface) -{ - thread_pool_impl_t *pool = (thread_pool_impl_t *)interface; - int status; - - pthread_mutex_lock(&pool->mtx); - status = pool->status; - pthread_mutex_unlock(&pool->mtx); - - return status; -} - -thread_pool_t *thread_pool_create(size_t num_jobs, thread_pool_worker_t worker) -{ - thread_pool_impl_t *pool; - thread_pool_t *interface; - sigset_t set, oldset; - size_t i, j; - int ret; - - if (num_jobs < 1) - num_jobs = 1; - - pool = alloc_flex(sizeof(*pool), sizeof(pool->workers[0]), num_jobs); - if (pool == NULL) - return NULL; - - if (pthread_mutex_init(&pool->mtx, NULL) != 0) - goto fail_free; - - if (pthread_cond_init(&pool->queue_cond, NULL) != 0) - goto fail_mtx; - - if (pthread_cond_init(&pool->done_cond, NULL) != 0) - goto fail_qcond; - - sigfillset(&set); - pthread_sigmask(SIG_SETMASK, &set, &oldset); - - pool->num_workers = num_jobs; - - for (i = 0; i < num_jobs; ++i) { - pool->workers[i].fun = worker; - pool->workers[i].pool = pool; - - ret = pthread_create(&pool->workers[i].thread, NULL, - worker_proc, pool->workers + i); - - if (ret != 0) - goto fail; - } - - pthread_sigmask(SIG_SETMASK, &oldset, NULL); - - interface = (thread_pool_t *)pool; - interface->destroy = destroy; - interface->get_worker_count = get_worker_count; - interface->set_worker_ptr = set_worker_ptr; - interface->submit = submit; - interface->dequeue = dequeue; - interface->get_status = get_status; - return interface; -fail: - pthread_mutex_lock(&pool->mtx); - pool->status = -1; - pthread_cond_broadcast(&pool->queue_cond); - pthread_mutex_unlock(&pool->mtx); - - for (j = 0; j < i; ++j) - pthread_join(pool->workers[j].thread, NULL); - - pthread_sigmask(SIG_SETMASK, &oldset, NULL); - pthread_cond_destroy(&pool->done_cond); -fail_qcond: - pthread_cond_destroy(&pool->queue_cond); -fail_mtx: - pthread_mutex_destroy(&pool->mtx); -fail_free: - free(pool); - return NULL; -} diff --git a/lib/util/threadpool_serial.c b/lib/util/threadpool_serial.c deleted file mode 100644 index fb24ee8..0000000 --- a/lib/util/threadpool_serial.c +++ /dev/null @@ -1,162 +0,0 @@ -/* SPDX-License-Identifier: LGPL-3.0-or-later */ -/* - * threadpool_serial.c - * - * Copyright (C) 2021 David Oberhollenzer - */ -#include "util/threadpool.h" -#include "util/util.h" - -#include -#include - -typedef struct work_item_t { - struct work_item_t *next; - void *data; -} work_item_t; - -typedef struct { - thread_pool_t base; - - work_item_t *queue; - work_item_t *queue_last; - - work_item_t *recycle; - - thread_pool_worker_t fun; - void *user; - int status; -} serial_impl_t; - -static void destroy(thread_pool_t *interface) -{ - serial_impl_t *pool = (serial_impl_t *)interface; - - while (pool->queue != NULL) { - work_item_t *item = pool->queue; - pool->queue = item->next; - free(item); - } - - while (pool->recycle != NULL) { - work_item_t *item = pool->recycle; - pool->recycle = item->next; - free(item); - } - - free(pool); -} - -static size_t get_worker_count(thread_pool_t *pool) -{ - (void)pool; - return 1; -} - -static void set_worker_ptr(thread_pool_t *interface, size_t idx, void *ptr) -{ - serial_impl_t *pool = (serial_impl_t *)interface; - - if (idx >= 1) - return; - - pool->user = ptr; -} - -static int submit(thread_pool_t *interface, void *ptr) -{ - serial_impl_t *pool = (serial_impl_t *)interface; - work_item_t *item = NULL; - - if (pool->status != 0) - return pool->status; - - if (pool->recycle != NULL) { - item = pool->recycle; - pool->recycle = item->next; - item->next = NULL; - } - - if (item == NULL) { - item = calloc(1, sizeof(*item)); - if (item == NULL) - return -1; - } - - item->data = ptr; - - if (pool->queue_last == NULL) { - pool->queue = item; - } else { - pool->queue_last->next = item; - } - - pool->queue_last = item; - return 0; -} - -static void *dequeue(thread_pool_t *interface) -{ - serial_impl_t *pool = (serial_impl_t *)interface; - work_item_t *item; - void *ptr; - int ret; - - if (pool->queue == NULL) - return NULL; - - item = pool->queue; - pool->queue = item->next; - - if (pool->queue == NULL) - pool->queue_last = NULL; - - ptr = item->data; - - memset(item, 0, sizeof(*item)); - item->next = pool->recycle; - pool->recycle = item; - - ret = pool->fun(pool->user, ptr); - - if (ret != 0 && pool->status == 0) - pool->status = ret; - - return ptr; -} - -static int get_status(thread_pool_t *interface) -{ - serial_impl_t *pool = (serial_impl_t *)interface; - - return pool->status; -} - -thread_pool_t *thread_pool_create_serial(thread_pool_worker_t worker) -{ - serial_impl_t *pool = calloc(1, sizeof(*pool)); - thread_pool_t *interface = (thread_pool_t *)pool; - - if (pool == NULL) - return NULL; - - pool->fun = worker; - - interface = (thread_pool_t *)pool; - interface->destroy = destroy; - interface->get_worker_count = get_worker_count; - interface->set_worker_ptr = set_worker_ptr; - interface->submit = submit; - interface->dequeue = dequeue; - interface->get_status = get_status; - return interface; - -} - -#ifdef NO_THREAD_IMPL -thread_pool_t *thread_pool_create(size_t num_jobs, thread_pool_worker_t worker) -{ - (void)num_jobs; - return thread_pool_create_serial(worker); -} -#endif diff --git a/lib/util/xxhash.c b/lib/util/xxhash.c deleted file mode 100644 index 60467fb..0000000 --- a/lib/util/xxhash.c +++ /dev/null @@ -1,112 +0,0 @@ -/* - * xxHash - Extremely Fast Hash algorithm - * Copyright (C) 2012-2016, Yann Collet. - * - * BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * ---------------------------------------------------------------------------- - * This code has been lifted from Linux (the OS kernel) and adapted for use in - * libsquashfs. For the original, unmodified and complete source, see below. - * - * You can contact the author at: - * - xxHash homepage: http://cyan4973.github.io/xxHash/ - * - xxHash source repository: https://github.com/Cyan4973/xxHash - */ -#include "config.h" -#include "util/util.h" - -#include - -#define xxh_rotl32(x, r) ((x << r) | (x >> (32 - r))) - -static const sqfs_u32 PRIME32_1 = 2654435761U; -static const sqfs_u32 PRIME32_2 = 2246822519U; -static const sqfs_u32 PRIME32_3 = 3266489917U; -static const sqfs_u32 PRIME32_4 = 668265263U; -static const sqfs_u32 PRIME32_5 = 374761393U; - -static sqfs_u32 xxh32_round(sqfs_u32 seed, sqfs_u32 input) -{ - seed += input * PRIME32_2; - seed = xxh_rotl32(seed, 13); - seed *= PRIME32_1; - return seed; -} - -static sqfs_u32 XXH_readLE32(const sqfs_u8 *ptr) -{ - sqfs_u32 value; - memcpy(&value, ptr, sizeof(value)); - return le32toh(value); -} - -sqfs_u32 xxh32(const void *input, const size_t len) -{ - const sqfs_u8 *p = (const sqfs_u8 *)input; - const sqfs_u8 *b_end = p + len; - sqfs_u32 h32; - - if (len >= 16) { - const sqfs_u8 *const limit = b_end - 16; - sqfs_u32 v1 = PRIME32_1 + PRIME32_2; - sqfs_u32 v2 = PRIME32_2; - sqfs_u32 v3 = 0; - sqfs_u32 v4 = PRIME32_1; - - do { - v1 = xxh32_round(v1, XXH_readLE32(p )); - v2 = xxh32_round(v2, XXH_readLE32(p + 4)); - v3 = xxh32_round(v3, XXH_readLE32(p + 8)); - v4 = xxh32_round(v4, XXH_readLE32(p + 12)); - p += 16; - } while (p <= limit); - - h32 = xxh_rotl32(v1, 1) + xxh_rotl32(v2, 7) + - xxh_rotl32(v3, 12) + xxh_rotl32(v4, 18); - } else { - h32 = PRIME32_5; - } - - h32 += (sqfs_u32)len; - - while (p + 4 <= b_end) { - h32 += XXH_readLE32(p) * PRIME32_3; - h32 = xxh_rotl32(h32, 17) * PRIME32_4; - p += 4; - } - - while (p < b_end) { - h32 += (*p) * PRIME32_5; - h32 = xxh_rotl32(h32, 11) * PRIME32_1; - p++; - } - - h32 ^= h32 >> 15; - h32 *= PRIME32_2; - h32 ^= h32 >> 13; - h32 *= PRIME32_3; - h32 ^= h32 >> 16; - return h32; -} diff --git a/lib/xfrm/Makemodule.am b/lib/xfrm/Makemodule.am index a344820..ad446f6 100644 --- a/lib/xfrm/Makemodule.am +++ b/lib/xfrm/Makemodule.am @@ -1,25 +1,25 @@ -libxfrm_a_SOURCES = include/xfrm/stream.h include/xfrm/compress.h -libxfrm_a_SOURCES += lib/xfrm/compress.c +libxfrm_a_SOURCES = include/xfrm/stream.h include/xfrm/compress.h \ + lib/xfrm/src/compress.c libxfrm_a_CFLAGS = $(AM_CFLAGS) if WITH_XZ -libxfrm_a_SOURCES += lib/xfrm/xz.c +libxfrm_a_SOURCES += lib/xfrm/src/xz.c libxfrm_a_CFLAGS += $(XZ_CFLAGS) -DWITH_XZ endif if WITH_BZIP2 -libxfrm_a_SOURCES += lib/xfrm/bzip2.c +libxfrm_a_SOURCES += lib/xfrm/src/bzip2.c libxfrm_a_CFLAGS += $(BZIP2_CFLAGS) -DWITH_BZIP2 endif if WITH_GZIP -libxfrm_a_SOURCES += lib/xfrm/gzip.c +libxfrm_a_SOURCES += lib/xfrm/src/gzip.c libxfrm_a_CFLAGS += $(ZLIB_CFLAGS) -DWITH_GZIP endif if WITH_ZSTD if HAVE_ZSTD_STREAM -libxfrm_a_SOURCES += lib/xfrm/zstd.c +libxfrm_a_SOURCES += lib/xfrm/src/zstd.c libxfrm_a_CFLAGS += $(ZSTD_CFLAGS) -DWITH_ZSTD endif endif diff --git a/lib/xfrm/bzip2.c b/lib/xfrm/bzip2.c deleted file mode 100644 index 7e5807d..0000000 --- a/lib/xfrm/bzip2.c +++ /dev/null @@ -1,151 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * bzip2.c - * - * Copyright (C) 2021 David Oberhollenzer - */ -#include -#include -#include - -#include "xfrm/stream.h" -#include "xfrm/compress.h" - -typedef struct { - xfrm_stream_t base; - - bz_stream strm; - bool compress; - bool initialized; - - int level; - int work_factor; -} xfrm_stream_bzip2_t; - -static const int bzlib_action[] = { - [XFRM_STREAM_FLUSH_NONE] = BZ_RUN, - [XFRM_STREAM_FLUSH_SYNC] = BZ_FLUSH, - [XFRM_STREAM_FLUSH_FULL] = BZ_FINISH, -}; - -static int process_data(xfrm_stream_t *stream, const void *in, sqfs_u32 in_size, - void *out, sqfs_u32 out_size, - sqfs_u32 *in_read, sqfs_u32 *out_written, - int flush_mode) -{ - xfrm_stream_bzip2_t *bzip2 = (xfrm_stream_bzip2_t *)stream; - sqfs_u32 diff; - int ret; - - if (!bzip2->initialized) { - if (bzip2->compress) { - ret = BZ2_bzCompressInit(&bzip2->strm, bzip2->level, 0, - bzip2->work_factor); - } else { - ret = BZ2_bzDecompressInit(&bzip2->strm, 0, 0); - } - - if (ret != BZ_OK) - return XFRM_STREAM_ERROR; - - bzip2->initialized = true; - } - - if (flush_mode < 0 || flush_mode >= XFRM_STREAM_FLUSH_COUNT) - flush_mode = XFRM_STREAM_FLUSH_NONE; - - while (in_size > 0 && out_size > 0) { - bzip2->strm.next_in = (char *)in; - bzip2->strm.avail_in = in_size; - - bzip2->strm.next_out = (char *)out; - bzip2->strm.avail_out = out_size; - - if (bzip2->compress) { - ret = BZ2_bzCompress(&bzip2->strm, - bzlib_action[flush_mode]); - } else { - ret = BZ2_bzDecompress(&bzip2->strm); - } - - if (ret == BZ_OUTBUFF_FULL) - return XFRM_STREAM_BUFFER_FULL; - - if (ret < 0) - return XFRM_STREAM_ERROR; - - diff = (in_size - bzip2->strm.avail_in); - in = (const char *)in + diff; - in_size -= diff; - *in_read += diff; - - diff = (out_size - bzip2->strm.avail_out); - out = (char *)out + diff; - out_size -= diff; - *out_written += diff; - - if (ret == BZ_STREAM_END) { - if (bzip2->compress) { - BZ2_bzCompressEnd(&bzip2->strm); - } else { - BZ2_bzDecompressEnd(&bzip2->strm); - } - - bzip2->initialized = false; - return XFRM_STREAM_END; - } - } - - return XFRM_STREAM_OK; -} - -static void destroy(sqfs_object_t *obj) -{ - xfrm_stream_bzip2_t *bzip2 = (xfrm_stream_bzip2_t *)obj; - - if (bzip2->initialized) { - if (bzip2->compress) { - BZ2_bzCompressEnd(&bzip2->strm); - } else { - BZ2_bzDecompressEnd(&bzip2->strm); - } - } - - free(bzip2); -} - -static xfrm_stream_t *stream_create(const compressor_config_t *cfg, - bool compress) -{ - xfrm_stream_bzip2_t *bzip2 = calloc(1, sizeof(*bzip2)); - xfrm_stream_t *xfrm = (xfrm_stream_t *)bzip2; - - if (bzip2 == NULL) { - perror("creating bzip2 stream compressor"); - return NULL; - } - - if (cfg == NULL) { - bzip2->level = COMP_BZIP2_DEFAULT_LEVEL; - bzip2->work_factor = COMP_BZIP2_DEFAULT_WORK_FACTOR; - } else { - bzip2->level = cfg->level; - bzip2->work_factor = cfg->opt.bzip2.work_factor; - } - - bzip2->initialized = false; - bzip2->compress = compress; - xfrm->process_data = process_data; - sqfs_object_init(bzip2, destroy, NULL); - return xfrm; -} - -xfrm_stream_t *compressor_stream_bzip2_create(const compressor_config_t *cfg) -{ - return stream_create(cfg, true); -} - -xfrm_stream_t *decompressor_stream_bzip2_create(void) -{ - return stream_create(NULL, false); -} diff --git a/lib/xfrm/compress.c b/lib/xfrm/compress.c deleted file mode 100644 index fbd6987..0000000 --- a/lib/xfrm/compress.c +++ /dev/null @@ -1,102 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * compress.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "xfrm/compress.h" -#include "config.h" - -#include - -static const struct { - int id; - const char *name; - const sqfs_u8 *magic; - size_t count; - xfrm_stream_t *(*mk_comp_stream)(const compressor_config_t *); - xfrm_stream_t *(*mk_decomp_stream)(void); -} compressors[] = { -#ifdef WITH_GZIP - { XFRM_COMPRESSOR_GZIP, "gzip", (const sqfs_u8 *)"\x1F\x8B\x08", 3, - compressor_stream_gzip_create, decompressor_stream_gzip_create }, -#endif -#ifdef WITH_XZ - { XFRM_COMPRESSOR_XZ, "xz", (const sqfs_u8 *)("\xFD" "7zXZ"), 6, - compressor_stream_xz_create, decompressor_stream_xz_create }, -#endif -#if defined(WITH_ZSTD) && defined(HAVE_ZSTD_STREAM) - { XFRM_COMPRESSOR_ZSTD, "zstd", - (const sqfs_u8 *)"\x28\xB5\x2F\xFD", 4, - compressor_stream_zstd_create, decompressor_stream_zstd_create }, -#endif -#ifdef WITH_BZIP2 - { XFRM_COMPRESSOR_BZIP2, "bzip2", (const sqfs_u8 *)"BZh", 3, - compressor_stream_bzip2_create, decompressor_stream_bzip2_create }, -#endif -}; - -int xfrm_compressor_id_from_name(const char *name) -{ - size_t i; - - for (i = 0; i < sizeof(compressors) / sizeof(compressors[0]); ++i) { - if (strcmp(name, compressors[i].name) == 0) - return compressors[i].id; - } - - return -1; -} - -int xfrm_compressor_id_from_magic(const void *data, size_t count) -{ - size_t i; - int ret; - - for (i = 0; i < sizeof(compressors) / sizeof(compressors[0]); ++i) { - if (compressors[i].count > count) - continue; - - ret = memcmp(compressors[i].magic, data, compressors[i].count); - if (ret == 0) - return compressors[i].id; - } - - return -1; -} - -const char *xfrm_compressor_name_from_id(int id) -{ - size_t i; - - for (i = 0; i < sizeof(compressors) / sizeof(compressors[0]); ++i) { - if (compressors[i].id == id) - return compressors[i].name; - } - - return NULL; -} - -xfrm_stream_t *compressor_stream_create(int id, const compressor_config_t *cfg) -{ - size_t i; - - for (i = 0; i < sizeof(compressors) / sizeof(compressors[0]); ++i) { - if (compressors[i].id == id) - return compressors[i].mk_comp_stream(cfg); - } - - return NULL; -} - -xfrm_stream_t *decompressor_stream_create(int id) -{ - size_t i; - - for (i = 0; i < sizeof(compressors) / sizeof(compressors[0]); ++i) { - if (compressors[i].id == id) - return compressors[i].mk_decomp_stream(); - } - - return NULL; -} diff --git a/lib/xfrm/gzip.c b/lib/xfrm/gzip.c deleted file mode 100644 index 67224f7..0000000 --- a/lib/xfrm/gzip.c +++ /dev/null @@ -1,144 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * gzip.c - * - * Copyright (C) 2021 David Oberhollenzer - */ -#include -#include -#include - -#include "xfrm/stream.h" -#include "xfrm/compress.h" - -typedef struct { - xfrm_stream_t base; - - z_stream strm; - bool compress; -} xfrm_stream_gzip_t; - -static const int zlib_action[] = { - [XFRM_STREAM_FLUSH_NONE] = Z_NO_FLUSH, - [XFRM_STREAM_FLUSH_SYNC] = Z_SYNC_FLUSH, - [XFRM_STREAM_FLUSH_FULL] = Z_FINISH, -}; - -static int process_data(xfrm_stream_t *stream, const void *in, - sqfs_u32 in_size, void *out, sqfs_u32 out_size, - sqfs_u32 *in_read, sqfs_u32 *out_written, - int flush_mode) -{ - xfrm_stream_gzip_t *gzip = (xfrm_stream_gzip_t *)stream; - sqfs_u32 diff; - int ret; - - if (flush_mode < 0 || flush_mode >= XFRM_STREAM_FLUSH_COUNT) - flush_mode = XFRM_STREAM_FLUSH_NONE; - - while (in_size > 0 && out_size > 0) { - gzip->strm.next_in = (void *)in; - gzip->strm.avail_in = in_size; - - gzip->strm.next_out = out; - gzip->strm.avail_out = out_size; - - if (gzip->compress) { - ret = deflate(&gzip->strm, zlib_action[flush_mode]); - } else { - ret = inflate(&gzip->strm, zlib_action[flush_mode]); - } - - if (ret == Z_STREAM_ERROR) - return XFRM_STREAM_ERROR; - - diff = in_size - gzip->strm.avail_in; - in = (const char *)in + diff; - in_size -= diff; - *in_read += diff; - - diff = out_size - gzip->strm.avail_out; - out = (char *)out + diff; - out_size -= diff; - *out_written += diff; - - if (ret == Z_STREAM_END) { - if (gzip->compress) { - ret = deflateReset(&gzip->strm); - } else { - ret = inflateReset(&gzip->strm); - } - - if (ret != Z_OK) - return XFRM_STREAM_ERROR; - - return XFRM_STREAM_END; - } - - if (ret == Z_BUF_ERROR) - return XFRM_STREAM_BUFFER_FULL; - } - - return XFRM_STREAM_OK; -} - -static void destroy(sqfs_object_t *obj) -{ - xfrm_stream_gzip_t *gzip = (xfrm_stream_gzip_t *)obj; - - if (gzip->compress) { - deflateEnd(&gzip->strm); - } else { - inflateEnd(&gzip->strm); - } - free(gzip); -} - -static xfrm_stream_t *create_stream(const compressor_config_t *cfg, - bool compress) -{ - xfrm_stream_gzip_t *gzip = calloc(1, sizeof(*gzip)); - xfrm_stream_t *xfrm = (xfrm_stream_t *)gzip; - int ret; - - if (gzip == NULL) { - perror("creating gzip stream compressor"); - return NULL; - } - - if (compress) { - int level = COMP_GZIP_DEFAULT_LEVEL; - int wnd = COMP_GZIP_DEFAULT_WINDOW; - - if (cfg != NULL) { - level = cfg->level; - wnd = cfg->opt.gzip.window_size; - } - - ret = deflateInit2(&gzip->strm, level, Z_DEFLATED, - wnd + 16, 8, Z_DEFAULT_STRATEGY); - } else { - ret = inflateInit2(&gzip->strm, 16 + 15); - } - - if (ret != Z_OK) { - fputs("internal error creating gzip compressor.\n", stderr); - free(gzip); - return NULL; - } - - gzip->compress = compress; - xfrm->process_data = process_data; - sqfs_object_init(xfrm, destroy, NULL); - return xfrm; -} - -xfrm_stream_t *compressor_stream_gzip_create(const compressor_config_t *cfg) -{ - return create_stream(cfg, true); -} - -xfrm_stream_t *decompressor_stream_gzip_create(void) -{ - return create_stream(NULL, false); -} diff --git a/lib/xfrm/src/bzip2.c b/lib/xfrm/src/bzip2.c new file mode 100644 index 0000000..7e5807d --- /dev/null +++ b/lib/xfrm/src/bzip2.c @@ -0,0 +1,151 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * bzip2.c + * + * Copyright (C) 2021 David Oberhollenzer + */ +#include +#include +#include + +#include "xfrm/stream.h" +#include "xfrm/compress.h" + +typedef struct { + xfrm_stream_t base; + + bz_stream strm; + bool compress; + bool initialized; + + int level; + int work_factor; +} xfrm_stream_bzip2_t; + +static const int bzlib_action[] = { + [XFRM_STREAM_FLUSH_NONE] = BZ_RUN, + [XFRM_STREAM_FLUSH_SYNC] = BZ_FLUSH, + [XFRM_STREAM_FLUSH_FULL] = BZ_FINISH, +}; + +static int process_data(xfrm_stream_t *stream, const void *in, sqfs_u32 in_size, + void *out, sqfs_u32 out_size, + sqfs_u32 *in_read, sqfs_u32 *out_written, + int flush_mode) +{ + xfrm_stream_bzip2_t *bzip2 = (xfrm_stream_bzip2_t *)stream; + sqfs_u32 diff; + int ret; + + if (!bzip2->initialized) { + if (bzip2->compress) { + ret = BZ2_bzCompressInit(&bzip2->strm, bzip2->level, 0, + bzip2->work_factor); + } else { + ret = BZ2_bzDecompressInit(&bzip2->strm, 0, 0); + } + + if (ret != BZ_OK) + return XFRM_STREAM_ERROR; + + bzip2->initialized = true; + } + + if (flush_mode < 0 || flush_mode >= XFRM_STREAM_FLUSH_COUNT) + flush_mode = XFRM_STREAM_FLUSH_NONE; + + while (in_size > 0 && out_size > 0) { + bzip2->strm.next_in = (char *)in; + bzip2->strm.avail_in = in_size; + + bzip2->strm.next_out = (char *)out; + bzip2->strm.avail_out = out_size; + + if (bzip2->compress) { + ret = BZ2_bzCompress(&bzip2->strm, + bzlib_action[flush_mode]); + } else { + ret = BZ2_bzDecompress(&bzip2->strm); + } + + if (ret == BZ_OUTBUFF_FULL) + return XFRM_STREAM_BUFFER_FULL; + + if (ret < 0) + return XFRM_STREAM_ERROR; + + diff = (in_size - bzip2->strm.avail_in); + in = (const char *)in + diff; + in_size -= diff; + *in_read += diff; + + diff = (out_size - bzip2->strm.avail_out); + out = (char *)out + diff; + out_size -= diff; + *out_written += diff; + + if (ret == BZ_STREAM_END) { + if (bzip2->compress) { + BZ2_bzCompressEnd(&bzip2->strm); + } else { + BZ2_bzDecompressEnd(&bzip2->strm); + } + + bzip2->initialized = false; + return XFRM_STREAM_END; + } + } + + return XFRM_STREAM_OK; +} + +static void destroy(sqfs_object_t *obj) +{ + xfrm_stream_bzip2_t *bzip2 = (xfrm_stream_bzip2_t *)obj; + + if (bzip2->initialized) { + if (bzip2->compress) { + BZ2_bzCompressEnd(&bzip2->strm); + } else { + BZ2_bzDecompressEnd(&bzip2->strm); + } + } + + free(bzip2); +} + +static xfrm_stream_t *stream_create(const compressor_config_t *cfg, + bool compress) +{ + xfrm_stream_bzip2_t *bzip2 = calloc(1, sizeof(*bzip2)); + xfrm_stream_t *xfrm = (xfrm_stream_t *)bzip2; + + if (bzip2 == NULL) { + perror("creating bzip2 stream compressor"); + return NULL; + } + + if (cfg == NULL) { + bzip2->level = COMP_BZIP2_DEFAULT_LEVEL; + bzip2->work_factor = COMP_BZIP2_DEFAULT_WORK_FACTOR; + } else { + bzip2->level = cfg->level; + bzip2->work_factor = cfg->opt.bzip2.work_factor; + } + + bzip2->initialized = false; + bzip2->compress = compress; + xfrm->process_data = process_data; + sqfs_object_init(bzip2, destroy, NULL); + return xfrm; +} + +xfrm_stream_t *compressor_stream_bzip2_create(const compressor_config_t *cfg) +{ + return stream_create(cfg, true); +} + +xfrm_stream_t *decompressor_stream_bzip2_create(void) +{ + return stream_create(NULL, false); +} diff --git a/lib/xfrm/src/compress.c b/lib/xfrm/src/compress.c new file mode 100644 index 0000000..fbd6987 --- /dev/null +++ b/lib/xfrm/src/compress.c @@ -0,0 +1,102 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * compress.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "xfrm/compress.h" +#include "config.h" + +#include + +static const struct { + int id; + const char *name; + const sqfs_u8 *magic; + size_t count; + xfrm_stream_t *(*mk_comp_stream)(const compressor_config_t *); + xfrm_stream_t *(*mk_decomp_stream)(void); +} compressors[] = { +#ifdef WITH_GZIP + { XFRM_COMPRESSOR_GZIP, "gzip", (const sqfs_u8 *)"\x1F\x8B\x08", 3, + compressor_stream_gzip_create, decompressor_stream_gzip_create }, +#endif +#ifdef WITH_XZ + { XFRM_COMPRESSOR_XZ, "xz", (const sqfs_u8 *)("\xFD" "7zXZ"), 6, + compressor_stream_xz_create, decompressor_stream_xz_create }, +#endif +#if defined(WITH_ZSTD) && defined(HAVE_ZSTD_STREAM) + { XFRM_COMPRESSOR_ZSTD, "zstd", + (const sqfs_u8 *)"\x28\xB5\x2F\xFD", 4, + compressor_stream_zstd_create, decompressor_stream_zstd_create }, +#endif +#ifdef WITH_BZIP2 + { XFRM_COMPRESSOR_BZIP2, "bzip2", (const sqfs_u8 *)"BZh", 3, + compressor_stream_bzip2_create, decompressor_stream_bzip2_create }, +#endif +}; + +int xfrm_compressor_id_from_name(const char *name) +{ + size_t i; + + for (i = 0; i < sizeof(compressors) / sizeof(compressors[0]); ++i) { + if (strcmp(name, compressors[i].name) == 0) + return compressors[i].id; + } + + return -1; +} + +int xfrm_compressor_id_from_magic(const void *data, size_t count) +{ + size_t i; + int ret; + + for (i = 0; i < sizeof(compressors) / sizeof(compressors[0]); ++i) { + if (compressors[i].count > count) + continue; + + ret = memcmp(compressors[i].magic, data, compressors[i].count); + if (ret == 0) + return compressors[i].id; + } + + return -1; +} + +const char *xfrm_compressor_name_from_id(int id) +{ + size_t i; + + for (i = 0; i < sizeof(compressors) / sizeof(compressors[0]); ++i) { + if (compressors[i].id == id) + return compressors[i].name; + } + + return NULL; +} + +xfrm_stream_t *compressor_stream_create(int id, const compressor_config_t *cfg) +{ + size_t i; + + for (i = 0; i < sizeof(compressors) / sizeof(compressors[0]); ++i) { + if (compressors[i].id == id) + return compressors[i].mk_comp_stream(cfg); + } + + return NULL; +} + +xfrm_stream_t *decompressor_stream_create(int id) +{ + size_t i; + + for (i = 0; i < sizeof(compressors) / sizeof(compressors[0]); ++i) { + if (compressors[i].id == id) + return compressors[i].mk_decomp_stream(); + } + + return NULL; +} diff --git a/lib/xfrm/src/gzip.c b/lib/xfrm/src/gzip.c new file mode 100644 index 0000000..67224f7 --- /dev/null +++ b/lib/xfrm/src/gzip.c @@ -0,0 +1,144 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * gzip.c + * + * Copyright (C) 2021 David Oberhollenzer + */ +#include +#include +#include + +#include "xfrm/stream.h" +#include "xfrm/compress.h" + +typedef struct { + xfrm_stream_t base; + + z_stream strm; + bool compress; +} xfrm_stream_gzip_t; + +static const int zlib_action[] = { + [XFRM_STREAM_FLUSH_NONE] = Z_NO_FLUSH, + [XFRM_STREAM_FLUSH_SYNC] = Z_SYNC_FLUSH, + [XFRM_STREAM_FLUSH_FULL] = Z_FINISH, +}; + +static int process_data(xfrm_stream_t *stream, const void *in, + sqfs_u32 in_size, void *out, sqfs_u32 out_size, + sqfs_u32 *in_read, sqfs_u32 *out_written, + int flush_mode) +{ + xfrm_stream_gzip_t *gzip = (xfrm_stream_gzip_t *)stream; + sqfs_u32 diff; + int ret; + + if (flush_mode < 0 || flush_mode >= XFRM_STREAM_FLUSH_COUNT) + flush_mode = XFRM_STREAM_FLUSH_NONE; + + while (in_size > 0 && out_size > 0) { + gzip->strm.next_in = (void *)in; + gzip->strm.avail_in = in_size; + + gzip->strm.next_out = out; + gzip->strm.avail_out = out_size; + + if (gzip->compress) { + ret = deflate(&gzip->strm, zlib_action[flush_mode]); + } else { + ret = inflate(&gzip->strm, zlib_action[flush_mode]); + } + + if (ret == Z_STREAM_ERROR) + return XFRM_STREAM_ERROR; + + diff = in_size - gzip->strm.avail_in; + in = (const char *)in + diff; + in_size -= diff; + *in_read += diff; + + diff = out_size - gzip->strm.avail_out; + out = (char *)out + diff; + out_size -= diff; + *out_written += diff; + + if (ret == Z_STREAM_END) { + if (gzip->compress) { + ret = deflateReset(&gzip->strm); + } else { + ret = inflateReset(&gzip->strm); + } + + if (ret != Z_OK) + return XFRM_STREAM_ERROR; + + return XFRM_STREAM_END; + } + + if (ret == Z_BUF_ERROR) + return XFRM_STREAM_BUFFER_FULL; + } + + return XFRM_STREAM_OK; +} + +static void destroy(sqfs_object_t *obj) +{ + xfrm_stream_gzip_t *gzip = (xfrm_stream_gzip_t *)obj; + + if (gzip->compress) { + deflateEnd(&gzip->strm); + } else { + inflateEnd(&gzip->strm); + } + free(gzip); +} + +static xfrm_stream_t *create_stream(const compressor_config_t *cfg, + bool compress) +{ + xfrm_stream_gzip_t *gzip = calloc(1, sizeof(*gzip)); + xfrm_stream_t *xfrm = (xfrm_stream_t *)gzip; + int ret; + + if (gzip == NULL) { + perror("creating gzip stream compressor"); + return NULL; + } + + if (compress) { + int level = COMP_GZIP_DEFAULT_LEVEL; + int wnd = COMP_GZIP_DEFAULT_WINDOW; + + if (cfg != NULL) { + level = cfg->level; + wnd = cfg->opt.gzip.window_size; + } + + ret = deflateInit2(&gzip->strm, level, Z_DEFLATED, + wnd + 16, 8, Z_DEFAULT_STRATEGY); + } else { + ret = inflateInit2(&gzip->strm, 16 + 15); + } + + if (ret != Z_OK) { + fputs("internal error creating gzip compressor.\n", stderr); + free(gzip); + return NULL; + } + + gzip->compress = compress; + xfrm->process_data = process_data; + sqfs_object_init(xfrm, destroy, NULL); + return xfrm; +} + +xfrm_stream_t *compressor_stream_gzip_create(const compressor_config_t *cfg) +{ + return create_stream(cfg, true); +} + +xfrm_stream_t *decompressor_stream_gzip_create(void) +{ + return create_stream(NULL, false); +} diff --git a/lib/xfrm/src/xz.c b/lib/xfrm/src/xz.c new file mode 100644 index 0000000..5adb6f3 --- /dev/null +++ b/lib/xfrm/src/xz.c @@ -0,0 +1,195 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * xz.c + * + * Copyright (C) 2021 David Oberhollenzer + */ +#include +#include +#include + +#include "xfrm/stream.h" +#include "xfrm/compress.h" + +typedef struct { + xfrm_stream_t base; + + lzma_stream strm; + + sqfs_u64 memlimit; + lzma_filter filters[3]; + lzma_options_lzma opt; + lzma_vli vli_filter; + sqfs_u32 presets; + + bool compress; + bool initialized; +} xfrm_xz_t; + +static const lzma_action xzlib_action[] = { + [XFRM_STREAM_FLUSH_NONE] = LZMA_RUN, + [XFRM_STREAM_FLUSH_SYNC] = LZMA_FULL_FLUSH, + [XFRM_STREAM_FLUSH_FULL] = LZMA_FINISH, +}; + +static lzma_vli vli_filter_from_flags(int vli) +{ + switch (vli) { + case COMP_XZ_VLI_X86: + return LZMA_FILTER_X86; + case COMP_XZ_VLI_POWERPC: + return LZMA_FILTER_POWERPC; + case COMP_XZ_VLI_IA64: + return LZMA_FILTER_IA64; + case COMP_XZ_VLI_ARM: + return LZMA_FILTER_ARM; + case COMP_XZ_VLI_ARMTHUMB: + return LZMA_FILTER_ARMTHUMB; + case COMP_XZ_VLI_SPARC: + return LZMA_FILTER_SPARC; + default: + return LZMA_VLI_UNKNOWN; + } +} + +static int process_data(xfrm_stream_t *stream, const void *in, + sqfs_u32 in_size, void *out, sqfs_u32 out_size, + sqfs_u32 *in_read, sqfs_u32 *out_written, + int flush_mode) +{ + xfrm_xz_t *xz = (xfrm_xz_t *)stream; + lzma_ret ret_xz; + sqfs_u32 diff; + + if (!xz->initialized) { + if (xz->compress) { + ret_xz = lzma_stream_encoder(&xz->strm, xz->filters, + LZMA_CHECK_CRC32); + } else { + ret_xz = lzma_stream_decoder(&xz->strm, + xz->memlimit, 0); + } + + if (ret_xz != LZMA_OK) + return XFRM_STREAM_ERROR; + + xz->initialized = true; + } + + if (flush_mode < 0 || flush_mode >= XFRM_STREAM_FLUSH_COUNT) + flush_mode = XFRM_STREAM_FLUSH_NONE; + + while (in_size > 0 && out_size > 0) { + xz->strm.next_in = in; + xz->strm.avail_in = in_size; + + xz->strm.next_out = out; + xz->strm.avail_out = out_size; + + ret_xz = lzma_code(&xz->strm, xzlib_action[flush_mode]); + + if (ret_xz != LZMA_OK && ret_xz != LZMA_BUF_ERROR && + ret_xz != LZMA_STREAM_END) { + return XFRM_STREAM_ERROR; + } + + diff = in_size - xz->strm.avail_in; + in = (const char *)in + diff; + in_size -= diff; + *in_read += diff; + + diff = out_size - xz->strm.avail_out; + out = (char *)out + diff; + out_size -= diff; + *out_written += diff; + + if (ret_xz == LZMA_BUF_ERROR) + return XFRM_STREAM_BUFFER_FULL; + + if (ret_xz == LZMA_STREAM_END) { + lzma_end(&xz->strm); + xz->initialized = false; + return XFRM_STREAM_END; + } + } + + return XFRM_STREAM_OK; +} + +static void destroy(sqfs_object_t *obj) +{ + xfrm_xz_t *xz = (xfrm_xz_t *)obj; + + if (xz->initialized) + lzma_end(&xz->strm); + + free(xz); +} + +static xfrm_stream_t *create_stream(const compressor_config_t *cfg, + bool compress) +{ + xfrm_xz_t *xz = calloc(1, sizeof(*xz)); + xfrm_stream_t *xfrm = (xfrm_stream_t *)xz; + int i = 0; + + if (xz == NULL) { + perror("creating xz stream compressor"); + return NULL; + } + + xz->memlimit = 128 * 1024 * 1024; + xz->compress = compress; + xz->initialized = false; + + if (compress) { + if (cfg == NULL) { + xz->presets = COMP_XZ_DEFAULT_LEVEL; + } else { + xz->presets = cfg->level; + if (cfg->flags & COMP_FLAG_XZ_EXTREME) + xz->presets |= LZMA_PRESET_EXTREME; + } + + if (lzma_lzma_preset(&xz->opt, xz->presets)) + goto fail_init; + + if (cfg == NULL) { + xz->vli_filter = LZMA_VLI_UNKNOWN; + } else { + xz->vli_filter = vli_filter_from_flags(cfg->opt.xz.vli); + } + + if (xz->vli_filter != LZMA_VLI_UNKNOWN) { + xz->filters[i].id = xz->vli_filter; + xz->filters[i].options = NULL; + ++i; + } + + xz->filters[i].id = LZMA_FILTER_LZMA2; + xz->filters[i].options = &xz->opt; + ++i; + + xz->filters[i].id = LZMA_VLI_UNKNOWN; + xz->filters[i].options = NULL; + ++i; + } + + xfrm->process_data = process_data; + sqfs_object_init(xz, destroy, NULL); + return xfrm; +fail_init: + fputs("error initializing XZ compressor\n", stderr); + free(xz); + return NULL; +} + +xfrm_stream_t *compressor_stream_xz_create(const compressor_config_t *cfg) +{ + return create_stream(cfg, true); +} + +xfrm_stream_t *decompressor_stream_xz_create(void) +{ + return create_stream(NULL, false); +} diff --git a/lib/xfrm/src/zstd.c b/lib/xfrm/src/zstd.c new file mode 100644 index 0000000..70666c1 --- /dev/null +++ b/lib/xfrm/src/zstd.c @@ -0,0 +1,136 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * zstd.c + * + * Copyright (C) 2021 David Oberhollenzer + */ +#include +#include +#include +#include + +#include "xfrm/stream.h" +#include "xfrm/compress.h" + +typedef struct { + xfrm_stream_t base; + + ZSTD_CStream *cstrm; + ZSTD_DStream *dstrm; + bool compress; +} xfrm_zstd_t; + +static const ZSTD_EndDirective zstd_action[] = { + [XFRM_STREAM_FLUSH_NONE] = ZSTD_e_continue, + [XFRM_STREAM_FLUSH_SYNC] = ZSTD_e_flush, + [XFRM_STREAM_FLUSH_FULL] = ZSTD_e_end, +}; + +static int process_data(xfrm_stream_t *stream, const void *in, + sqfs_u32 in_size, void *out, sqfs_u32 out_size, + sqfs_u32 *in_read, sqfs_u32 *out_written, + int flush_mode) +{ + xfrm_zstd_t *zstd = (xfrm_zstd_t *)stream; + ZSTD_outBuffer out_desc; + ZSTD_inBuffer in_desc; + size_t ret; + + if (flush_mode < 0 || flush_mode >= XFRM_STREAM_FLUSH_COUNT) + flush_mode = XFRM_STREAM_FLUSH_NONE; + + while (in_size > 0 && out_size > 0) { + memset(&in_desc, 0, sizeof(in_desc)); + in_desc.src = in; + in_desc.size = in_size; + + memset(&out_desc, 0, sizeof(out_desc)); + out_desc.dst = out; + out_desc.size = out_size; + + if (zstd->compress) { + ret = ZSTD_compressStream2(zstd->cstrm, &out_desc, + &in_desc, + zstd_action[flush_mode]); + } else { + ret = ZSTD_decompressStream(zstd->dstrm, &out_desc, + &in_desc); + } + + if (ZSTD_isError(ret)) + return XFRM_STREAM_ERROR; + + in = (const char *)in + in_desc.pos; + in_size -= in_desc.pos; + *in_read += in_desc.pos; + + out = (char *)out + out_desc.pos; + out_size -= out_desc.pos; + *out_written += out_desc.pos; + } + + if (flush_mode != XFRM_STREAM_FLUSH_NONE) { + if (in_size == 0) + return XFRM_STREAM_END; + } + + if (in_size > 0 && out_size == 0) + return XFRM_STREAM_BUFFER_FULL; + + return XFRM_STREAM_OK; +} + +static void destroy(sqfs_object_t *obj) +{ + xfrm_zstd_t *zstd = (xfrm_zstd_t *)obj; + + if (zstd->compress) { + ZSTD_freeCStream(zstd->cstrm); + } else { + ZSTD_freeDStream(zstd->dstrm); + } + + free(zstd); +} + +static xfrm_stream_t *stream_create(const compressor_config_t *cfg, + bool compress) +{ + xfrm_zstd_t *zstd = calloc(1, sizeof(*zstd)); + xfrm_stream_t *strm = (xfrm_stream_t *)zstd; + (void)cfg; + + if (zstd == NULL) { + perror("creating zstd stream compressor"); + return NULL; + } + + if (compress) { + zstd->cstrm = ZSTD_createCStream(); + if (zstd->cstrm == NULL) + goto fail_strm; + } else { + zstd->dstrm = ZSTD_createDStream(); + if (zstd->dstrm == NULL) + goto fail_strm; + } + + zstd->compress = compress; + strm->process_data = process_data; + sqfs_object_init(zstd, destroy, NULL); + return strm; +fail_strm: + fputs("error initializing zstd stream.\n", stderr); + free(zstd); + return NULL; +} + +xfrm_stream_t *compressor_stream_zstd_create(const compressor_config_t *cfg) +{ + return stream_create(cfg, true); +} + +xfrm_stream_t *decompressor_stream_zstd_create(void) +{ + return stream_create(NULL, false); +} diff --git a/lib/xfrm/xz.c b/lib/xfrm/xz.c deleted file mode 100644 index 5adb6f3..0000000 --- a/lib/xfrm/xz.c +++ /dev/null @@ -1,195 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * xz.c - * - * Copyright (C) 2021 David Oberhollenzer - */ -#include -#include -#include - -#include "xfrm/stream.h" -#include "xfrm/compress.h" - -typedef struct { - xfrm_stream_t base; - - lzma_stream strm; - - sqfs_u64 memlimit; - lzma_filter filters[3]; - lzma_options_lzma opt; - lzma_vli vli_filter; - sqfs_u32 presets; - - bool compress; - bool initialized; -} xfrm_xz_t; - -static const lzma_action xzlib_action[] = { - [XFRM_STREAM_FLUSH_NONE] = LZMA_RUN, - [XFRM_STREAM_FLUSH_SYNC] = LZMA_FULL_FLUSH, - [XFRM_STREAM_FLUSH_FULL] = LZMA_FINISH, -}; - -static lzma_vli vli_filter_from_flags(int vli) -{ - switch (vli) { - case COMP_XZ_VLI_X86: - return LZMA_FILTER_X86; - case COMP_XZ_VLI_POWERPC: - return LZMA_FILTER_POWERPC; - case COMP_XZ_VLI_IA64: - return LZMA_FILTER_IA64; - case COMP_XZ_VLI_ARM: - return LZMA_FILTER_ARM; - case COMP_XZ_VLI_ARMTHUMB: - return LZMA_FILTER_ARMTHUMB; - case COMP_XZ_VLI_SPARC: - return LZMA_FILTER_SPARC; - default: - return LZMA_VLI_UNKNOWN; - } -} - -static int process_data(xfrm_stream_t *stream, const void *in, - sqfs_u32 in_size, void *out, sqfs_u32 out_size, - sqfs_u32 *in_read, sqfs_u32 *out_written, - int flush_mode) -{ - xfrm_xz_t *xz = (xfrm_xz_t *)stream; - lzma_ret ret_xz; - sqfs_u32 diff; - - if (!xz->initialized) { - if (xz->compress) { - ret_xz = lzma_stream_encoder(&xz->strm, xz->filters, - LZMA_CHECK_CRC32); - } else { - ret_xz = lzma_stream_decoder(&xz->strm, - xz->memlimit, 0); - } - - if (ret_xz != LZMA_OK) - return XFRM_STREAM_ERROR; - - xz->initialized = true; - } - - if (flush_mode < 0 || flush_mode >= XFRM_STREAM_FLUSH_COUNT) - flush_mode = XFRM_STREAM_FLUSH_NONE; - - while (in_size > 0 && out_size > 0) { - xz->strm.next_in = in; - xz->strm.avail_in = in_size; - - xz->strm.next_out = out; - xz->strm.avail_out = out_size; - - ret_xz = lzma_code(&xz->strm, xzlib_action[flush_mode]); - - if (ret_xz != LZMA_OK && ret_xz != LZMA_BUF_ERROR && - ret_xz != LZMA_STREAM_END) { - return XFRM_STREAM_ERROR; - } - - diff = in_size - xz->strm.avail_in; - in = (const char *)in + diff; - in_size -= diff; - *in_read += diff; - - diff = out_size - xz->strm.avail_out; - out = (char *)out + diff; - out_size -= diff; - *out_written += diff; - - if (ret_xz == LZMA_BUF_ERROR) - return XFRM_STREAM_BUFFER_FULL; - - if (ret_xz == LZMA_STREAM_END) { - lzma_end(&xz->strm); - xz->initialized = false; - return XFRM_STREAM_END; - } - } - - return XFRM_STREAM_OK; -} - -static void destroy(sqfs_object_t *obj) -{ - xfrm_xz_t *xz = (xfrm_xz_t *)obj; - - if (xz->initialized) - lzma_end(&xz->strm); - - free(xz); -} - -static xfrm_stream_t *create_stream(const compressor_config_t *cfg, - bool compress) -{ - xfrm_xz_t *xz = calloc(1, sizeof(*xz)); - xfrm_stream_t *xfrm = (xfrm_stream_t *)xz; - int i = 0; - - if (xz == NULL) { - perror("creating xz stream compressor"); - return NULL; - } - - xz->memlimit = 128 * 1024 * 1024; - xz->compress = compress; - xz->initialized = false; - - if (compress) { - if (cfg == NULL) { - xz->presets = COMP_XZ_DEFAULT_LEVEL; - } else { - xz->presets = cfg->level; - if (cfg->flags & COMP_FLAG_XZ_EXTREME) - xz->presets |= LZMA_PRESET_EXTREME; - } - - if (lzma_lzma_preset(&xz->opt, xz->presets)) - goto fail_init; - - if (cfg == NULL) { - xz->vli_filter = LZMA_VLI_UNKNOWN; - } else { - xz->vli_filter = vli_filter_from_flags(cfg->opt.xz.vli); - } - - if (xz->vli_filter != LZMA_VLI_UNKNOWN) { - xz->filters[i].id = xz->vli_filter; - xz->filters[i].options = NULL; - ++i; - } - - xz->filters[i].id = LZMA_FILTER_LZMA2; - xz->filters[i].options = &xz->opt; - ++i; - - xz->filters[i].id = LZMA_VLI_UNKNOWN; - xz->filters[i].options = NULL; - ++i; - } - - xfrm->process_data = process_data; - sqfs_object_init(xz, destroy, NULL); - return xfrm; -fail_init: - fputs("error initializing XZ compressor\n", stderr); - free(xz); - return NULL; -} - -xfrm_stream_t *compressor_stream_xz_create(const compressor_config_t *cfg) -{ - return create_stream(cfg, true); -} - -xfrm_stream_t *decompressor_stream_xz_create(void) -{ - return create_stream(NULL, false); -} diff --git a/lib/xfrm/zstd.c b/lib/xfrm/zstd.c deleted file mode 100644 index 70666c1..0000000 --- a/lib/xfrm/zstd.c +++ /dev/null @@ -1,136 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * zstd.c - * - * Copyright (C) 2021 David Oberhollenzer - */ -#include -#include -#include -#include - -#include "xfrm/stream.h" -#include "xfrm/compress.h" - -typedef struct { - xfrm_stream_t base; - - ZSTD_CStream *cstrm; - ZSTD_DStream *dstrm; - bool compress; -} xfrm_zstd_t; - -static const ZSTD_EndDirective zstd_action[] = { - [XFRM_STREAM_FLUSH_NONE] = ZSTD_e_continue, - [XFRM_STREAM_FLUSH_SYNC] = ZSTD_e_flush, - [XFRM_STREAM_FLUSH_FULL] = ZSTD_e_end, -}; - -static int process_data(xfrm_stream_t *stream, const void *in, - sqfs_u32 in_size, void *out, sqfs_u32 out_size, - sqfs_u32 *in_read, sqfs_u32 *out_written, - int flush_mode) -{ - xfrm_zstd_t *zstd = (xfrm_zstd_t *)stream; - ZSTD_outBuffer out_desc; - ZSTD_inBuffer in_desc; - size_t ret; - - if (flush_mode < 0 || flush_mode >= XFRM_STREAM_FLUSH_COUNT) - flush_mode = XFRM_STREAM_FLUSH_NONE; - - while (in_size > 0 && out_size > 0) { - memset(&in_desc, 0, sizeof(in_desc)); - in_desc.src = in; - in_desc.size = in_size; - - memset(&out_desc, 0, sizeof(out_desc)); - out_desc.dst = out; - out_desc.size = out_size; - - if (zstd->compress) { - ret = ZSTD_compressStream2(zstd->cstrm, &out_desc, - &in_desc, - zstd_action[flush_mode]); - } else { - ret = ZSTD_decompressStream(zstd->dstrm, &out_desc, - &in_desc); - } - - if (ZSTD_isError(ret)) - return XFRM_STREAM_ERROR; - - in = (const char *)in + in_desc.pos; - in_size -= in_desc.pos; - *in_read += in_desc.pos; - - out = (char *)out + out_desc.pos; - out_size -= out_desc.pos; - *out_written += out_desc.pos; - } - - if (flush_mode != XFRM_STREAM_FLUSH_NONE) { - if (in_size == 0) - return XFRM_STREAM_END; - } - - if (in_size > 0 && out_size == 0) - return XFRM_STREAM_BUFFER_FULL; - - return XFRM_STREAM_OK; -} - -static void destroy(sqfs_object_t *obj) -{ - xfrm_zstd_t *zstd = (xfrm_zstd_t *)obj; - - if (zstd->compress) { - ZSTD_freeCStream(zstd->cstrm); - } else { - ZSTD_freeDStream(zstd->dstrm); - } - - free(zstd); -} - -static xfrm_stream_t *stream_create(const compressor_config_t *cfg, - bool compress) -{ - xfrm_zstd_t *zstd = calloc(1, sizeof(*zstd)); - xfrm_stream_t *strm = (xfrm_stream_t *)zstd; - (void)cfg; - - if (zstd == NULL) { - perror("creating zstd stream compressor"); - return NULL; - } - - if (compress) { - zstd->cstrm = ZSTD_createCStream(); - if (zstd->cstrm == NULL) - goto fail_strm; - } else { - zstd->dstrm = ZSTD_createDStream(); - if (zstd->dstrm == NULL) - goto fail_strm; - } - - zstd->compress = compress; - strm->process_data = process_data; - sqfs_object_init(zstd, destroy, NULL); - return strm; -fail_strm: - fputs("error initializing zstd stream.\n", stderr); - free(zstd); - return NULL; -} - -xfrm_stream_t *compressor_stream_zstd_create(const compressor_config_t *cfg) -{ - return stream_create(cfg, true); -} - -xfrm_stream_t *decompressor_stream_zstd_create(void) -{ - return stream_create(NULL, false); -} diff --git a/tests/gensquashfs/Makemodule.am b/tests/gensquashfs/Makemodule.am index 553f2a8..9dd9d76 100644 --- a/tests/gensquashfs/Makemodule.am +++ b/tests/gensquashfs/Makemodule.am @@ -1,49 +1,51 @@ GENDATADIR=$(top_srcdir)/tests/gensquashfs test_filemap_xattr_SOURCES = tests/gensquashfs/filemap_xattr.c \ - bin/gensquashfs/filemap_xattr.c \ - bin/gensquashfs/mkfs.h -test_filemap_xattr_CPPFLAGS = $(AM_CPPFLAGS) -I$(top_srcdir)/bin/gensquashfs + bin/gensquashfs/src/filemap_xattr.c \ + bin/gensquashfs/src/mkfs.h +test_filemap_xattr_CPPFLAGS = $(AM_CPPFLAGS) -I$(top_srcdir)/bin/gensquashfs/src test_filemap_xattr_CPPFLAGS += -DTESTPATH=$(GENDATADIR)/xattr1.txt test_filemap_xattr_LDADD = libsquashfs.la libfstree.a libutil.a test_filemap_xattr_LDADD += libio.a libcompat.a test_fstree_from_file_SOURCES = tests/gensquashfs/fstree_from_file.c \ - bin/gensquashfs/fstree_from_file.c \ - bin/gensquashfs/fstree_from_dir.c \ - bin/gensquashfs/mkfs.h -test_fstree_from_file_CPPFLAGS = $(AM_CPPFLAGS) -I$(top_srcdir)/bin/gensquashfs + bin/gensquashfs/src/fstree_from_file.c \ + bin/gensquashfs/src/fstree_from_dir.c \ + bin/gensquashfs/src/mkfs.h +test_fstree_from_file_CPPFLAGS = $(AM_CPPFLAGS) +test_fstree_from_file_CPPFLAGS += -I$(top_srcdir)/bin/gensquashfs/src test_fstree_from_file_CPPFLAGS += -DTESTPATH=$(GENDATADIR)/fstree1.txt test_fstree_from_file_LDADD = libfstree.a libio.a libutil.a libcompat.a test_fstree_glob1_SOURCES = tests/gensquashfs/fstree_glob1.c \ - bin/gensquashfs/fstree_from_file.c \ - bin/gensquashfs/fstree_from_dir.c \ - bin/gensquashfs/mkfs.h -test_fstree_glob1_CPPFLAGS = $(AM_CPPFLAGS) -I$(top_srcdir)/bin/gensquashfs + bin/gensquashfs/src/fstree_from_file.c \ + bin/gensquashfs/src/fstree_from_dir.c \ + bin/gensquashfs/src/mkfs.h +test_fstree_glob1_CPPFLAGS = $(AM_CPPFLAGS) -I$(top_srcdir)/bin/gensquashfs/src test_fstree_glob1_CPPFLAGS += -DTESTPATH=$(GENDATADIR) test_fstree_glob1_LDADD = libfstree.a libio.a libutil.a libcompat.a test_fstree_from_dir_SOURCES = tests/gensquashfs/fstree_from_dir.c \ - bin/gensquashfs/fstree_from_dir.c \ - bin/gensquashfs/mkfs.h -test_fstree_from_dir_CPPFLAGS = $(AM_CPPFLAGS) -I$(top_srcdir)/bin/gensquashfs + bin/gensquashfs/src/fstree_from_dir.c \ + bin/gensquashfs/src/mkfs.h +test_fstree_from_dir_CPPFLAGS = $(AM_CPPFLAGS) +test_fstree_from_dir_CPPFLAGS += -I$(top_srcdir)/bin/gensquashfs/src test_fstree_from_dir_CPPFLAGS += -DTESTPATH=$(GENDATADIR)/testdir test_fstree_from_dir_LDADD = libfstree.a libutil.a libcompat.a test_sort_file_SOURCES = tests/gensquashfs/sort_file.c \ - bin/gensquashfs/fstree_from_file.c \ - bin/gensquashfs/fstree_from_dir.c \ - bin/gensquashfs/sort_by_file.c \ - bin/gensquashfs/mkfs.h -test_sort_file_CPPFLAGS = $(AM_CPPFLAGS) -I$(top_srcdir)/bin/gensquashfs + bin/gensquashfs/src/fstree_from_file.c \ + bin/gensquashfs/src/fstree_from_dir.c \ + bin/gensquashfs/src/sort_by_file.c \ + bin/gensquashfs/src/mkfs.h +test_sort_file_CPPFLAGS = $(AM_CPPFLAGS) -I$(top_srcdir)/bin/gensquashfs/src test_sort_file_LDADD = libfstree.a libio.a libutil.a libcompat.a fstree_fuzz_SOURCES = tests/gensquashfs/fstree_fuzz.c \ - bin/gensquashfs/fstree_from_file.c \ - bin/gensquashfs/fstree_from_dir.c \ - bin/gensquashfs/mkfs.h -fstree_fuzz_CPPFLAGS = $(AM_CPPFLAGS) -I$(top_srcdir)/bin/gensquashfs + bin/gensquashfs/src/fstree_from_file.c \ + bin/gensquashfs/src/fstree_from_dir.c \ + bin/gensquashfs/src/mkfs.h +fstree_fuzz_CPPFLAGS = $(AM_CPPFLAGS) -I$(top_srcdir)/bin/gensquashfs/src fstree_fuzz_LDADD = libfstree.a libio.a libutil.a libcompat.a GENSQUASHFS_TESTS = \ diff --git a/tests/libutil/Makemodule.am b/tests/libutil/Makemodule.am index c84f722..4cec465 100644 --- a/tests/libutil/Makemodule.am +++ b/tests/libutil/Makemodule.am @@ -24,11 +24,11 @@ test_canonicalize_name_SOURCES = tests/libutil/canonicalize_name.c test_canonicalize_name_LDADD = libutil.a libcompat.a test_filename_sane_SOURCES = tests/libutil/filename_sane.c -test_filename_sane_SOURCES += lib/util/filename_sane.c +test_filename_sane_SOURCES += lib/util/src/filename_sane.c test_filename_sane_LDADD = libcompat.a libutil.a test_filename_sane_w32_SOURCES = tests/libutil/filename_sane.c -test_filename_sane_w32_SOURCES += lib/util/filename_sane.c +test_filename_sane_w32_SOURCES += lib/util/src/filename_sane.c test_filename_sane_w32_CPPFLAGS = $(AM_CPPFLAGS) -DTEST_WIN32=1 test_filename_sane_w32_LDADD = libcompat.a -- cgit v1.2.3