%% %% %CopyrightBegin% %% %% Copyright Ericsson AB 1997-2018. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. %% You may obtain a copy of the License at %% %% http://www.apache.org/licenses/LICENSE-2.0 %% %% Unless required by applicable law or agreed to in writing, software %% distributed under the License is distributed on an "AS IS" BASIS, %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% See the License for the specific language governing permissions and %% limitations under the License. %% %% %CopyrightEnd% %% Options used when adding files to a tar archive. -record(add_opts, { read_info, %% Fun to use for read file/link info. chunk_size = 0, %% For file reading when sending to sftp. 0=do not chunk verbose = false, %% Verbose on/off. atime = undefined, mtime = undefined, ctime = undefined, uid = 0, gid = 0}). -type add_opts() :: #add_opts{}. %% Options used when reading a tar archive. -record(read_opts, { cwd :: string(), %% Current working directory. keep_old_files = false :: boolean(), %% Owerwrite or not. files = all, %% Set of files to extract (or all) output = file :: 'file' | 'memory', open_mode = [], %% Open mode options. verbose = false :: boolean()}). %% Verbose on/off. -type read_opts() :: #read_opts{}. -type add_opt() :: dereference | verbose | {chunks, pos_integer()} | {atime, non_neg_integer()} | {mtime, non_neg_integer()} | {ctime, non_neg_integer()} | {uid, non_neg_integer()} | {gid, non_neg_integer()}. -type extract_opt() :: {cwd, string()} | {files, [string()]} | compressed | cooked | memory | keep_old_files | verbose. -type create_opt() :: compressed | cooked | dereference | verbose. -type filelist() :: [file:filename() | {string(), binary()} | {string(), file:filename()}]. -type tar_time() :: non_neg_integer(). %% The tar header, once fully parsed. -record(tar_header, { name = "" :: string(), %% name of header file entry mode = 8#100644 :: non_neg_integer(), %% permission and mode bits uid = 0 :: non_neg_integer(), %% user id of owner gid = 0 :: non_neg_integer(), %% group id of owner size = 0 :: non_neg_integer(), %% length in bytes mtime :: tar_time(), %% modified time typeflag :: char(), %% type of header entry linkname = "" :: string(), %% target name of link uname = "" :: string(), %% user name of owner gname = "" :: string(), %% group name of owner devmajor = 0 :: non_neg_integer(), %% major number of character or block device devminor = 0 :: non_neg_integer(), %% minor number of character or block device atime :: tar_time(), %% access time ctime :: tar_time() %% status change time }). -type tar_header() :: #tar_header{}. %% Metadata for a sparse file fragment -record(sparse_entry, { offset = 0 :: non_neg_integer(), num_bytes = 0 :: non_neg_integer()}). -type sparse_entry() :: #sparse_entry{}. %% Contains metadata about fragments of a sparse file -record(sparse_array, { entries = [] :: [sparse_entry()], is_extended = false :: boolean(), max_entries = 0 :: non_neg_integer()}). -type sparse_array() :: #sparse_array{}. %% A subset of tar header fields common to all tar implementations -record(header_v7, { name :: binary(), mode :: binary(), %% octal uid :: binary(), %% integer gid :: binary(), %% integer size :: binary(), %% integer mtime :: binary(), %% integer checksum :: binary(), %% integer typeflag :: byte(), %% char linkname :: binary()}). -type header_v7() :: #header_v7{}. %% The set of fields specific to GNU tar formatted archives -record(header_gnu, { header_v7 :: header_v7(), magic :: binary(), version :: binary(), uname :: binary(), gname :: binary(), devmajor :: binary(), %% integer devminor :: binary(), %% integer atime :: binary(), %% integer ctime :: binary(), %% integer sparse :: sparse_array(), real_size :: binary()}). %% integer -type header_gnu() :: #header_gnu{}. %% The set of fields specific to STAR-formatted archives -record(header_star, { header_v7 :: header_v7(), magic :: binary(), version :: binary(), uname :: binary(), gname :: binary(), devmajor :: binary(), %% integer devminor :: binary(), %% integer prefix :: binary(), atime :: binary(), %% integer ctime :: binary(), %% integer trailer :: binary()}). -type header_star() :: #header_star{}. %% The set of fields specific to USTAR-formatted archives -record(header_ustar, { header_v7 :: header_v7(), magic :: binary(), version :: binary(), uname :: binary(), gname :: binary(), devmajor :: binary(), %% integer devminor :: binary(), %% integer prefix :: binary()}). -type header_ustar() :: #header_ustar{}. -type header_fields() :: header_v7() | header_gnu() | header_star() | header_ustar(). %% The overall tar reader, it holds the low-level file handle, %% its access, position, and the I/O primitives wrapper. -record(reader, { handle :: file:io_device() | term(), access :: read | write | ram, pos = 0 :: non_neg_integer(), func :: file_op() }). -type reader() :: #reader{}. %% A reader for a regular file within the tar archive, %% It tracks its current state relative to that file. -record(reg_file_reader, { handle :: reader(), num_bytes = 0, pos = 0, size = 0 }). -type reg_file_reader() :: #reg_file_reader{}. %% A reader for a sparse file within the tar archive, %% It tracks its current state relative to that file. -record(sparse_file_reader, { handle :: reader(), num_bytes = 0, %% bytes remaining pos = 0, %% pos size = 0, %% total size of file sparse_map = #sparse_array{} }). -type sparse_file_reader() :: #sparse_file_reader{}. %% Types for the readers -type reader_type() :: reader() | reg_file_reader() | sparse_file_reader(). -type handle() :: file:io_device() | term(). %% Type for the I/O primitive wrapper function -type file_op() :: fun((write | close | read2 | position, {handle(), iodata()} | handle() | {handle(), non_neg_integer()} | {handle(), non_neg_integer()}) -> ok | eof | {ok, string() | binary()} | {ok, non_neg_integer()} | {error, term()}). %% These constants (except S_IFMT) are %% used to determine what type of device %% a file is. Namely, `S_IFMT band file_info.mode` %% will equal one of these contants, and tells us %% which type it is. The stdlib file_info record %% does not differentiate between device types, and %% will not allow us to differentiate between sockets %% and named pipes. These constants are pulled from libc. -define(S_IFMT, 61440). -define(S_IFSOCK, 49152). %% socket -define(S_FIFO, 4096). %% fifo/named pipe -define(S_IFBLK, 24576). %% block device -define(S_IFCHR, 8192). %% character device %% Typeflag constants for the tar header -define(TYPE_REGULAR, $0). %% regular file -define(TYPE_REGULAR_A, 0). %% regular file -define(TYPE_LINK, $1). %% hard link -define(TYPE_SYMLINK, $2). %% symbolic link -define(TYPE_CHAR, $3). %% character device node -define(TYPE_BLOCK, $4). %% block device node -define(TYPE_DIR, $5). %% directory -define(TYPE_FIFO, $6). %% fifo node -define(TYPE_CONT, $7). %% reserved -define(TYPE_X_HEADER, $x). %% extended header -define(TYPE_X_GLOBAL_HEADER, $g). %% global extended header -define(TYPE_GNU_LONGNAME, $L). %% next file has a long name -define(TYPE_GNU_LONGLINK, $K). %% next file symlinks to a file with a long name -define(TYPE_GNU_SPARSE, $S). %% sparse file %% Mode constants from tar spec -define(MODE_ISUID, 4000). %% set uid -define(MODE_ISGID, 2000). %% set gid -define(MODE_ISVTX, 1000). %% save text (sticky bit) -define(MODE_ISDIR, 40000). %% directory -define(MODE_ISFIFO, 10000). %% fifo -define(MODE_ISREG, 100000). %% regular file -define(MODE_ISLNK, 120000). %% symbolic link -define(MODE_ISBLK, 60000). %% block special file -define(MODE_ISCHR, 20000). %% character special file -define(MODE_ISSOCK, 140000). %% socket %% Keywords for PAX extended header -define(PAX_ATIME, <<"atime">>). -define(PAX_CHARSET, <<"charset">>). -define(PAX_COMMENT, <<"comment">>). -define(PAX_CTIME, <<"ctime">>). %% ctime is not a valid pax header -define(PAX_GID, <<"gid">>). -define(PAX_GNAME, <<"gname">>). -define(PAX_LINKPATH, <<"linkpath">>). -define(PAX_MTIME, <<"mtime">>). -define(PAX_PATH, <<"path">>). -define(PAX_SIZE, <<"size">>). -define(PAX_UID, <<"uid">>). -define(PAX_UNAME, <<"uname">>). -define(PAX_XATTR, <<"SCHILY.xattr.">>). -define(PAX_XATTR_STR, "SCHILY.xattr."). -define(PAX_NONE, <<"">>). %% Tar format constants %% Unknown format -define(FORMAT_UNKNOWN, 0). %% The format of the original Unix V7 tar tool prior to standardization -define(FORMAT_V7, 1). %% The old and new GNU formats, incompatible with USTAR. %% This covers the old GNU sparse extension, but it does %% not cover the GNU sparse extensions using PAX headers, %% versions 0.0, 0.1, and 1.0; these fall under the PAX format. -define(FORMAT_GNU, 2). %% Schily's tar format, which is incompatible with USTAR. %% This does not cover STAR extensions to the PAX format; these %% fall under the PAX format. -define(FORMAT_STAR, 3). %% USTAR is the former standardization of tar defined in POSIX.1-1988, %% it is incompatible with the GNU and STAR formats. -define(FORMAT_USTAR, 4). %% PAX is the latest standardization of tar defined in POSIX.1-2001. %% This is an extension of USTAR and is "backwards compatible" with it. %% %% Some newer formats add their own extensions to PAX, such as GNU sparse %% files and SCHILY extended attributes. Since they are backwards compatible %% with PAX, they will be labelled as "PAX". -define(FORMAT_PAX, 5). %% Magic constants -define(MAGIC_GNU, <<"ustar ">>). -define(VERSION_GNU, <<" \x00">>). -define(MAGIC_USTAR, <<"ustar\x00">>). -define(VERSION_USTAR, <<"00">>). -define(TRAILER_STAR, <<"tar\x00">>). %% Size constants -define(BLOCK_SIZE, 512). %% size of each block in a tar stream -define(NAME_SIZE, 100). %% max length of the name field in USTAR format -define(PREFIX_SIZE, 155). %% max length of the prefix field in USTAR format %% Maximum size of a nanosecond value as an integer -define(MAX_NANO_INT_SIZE, 9). %% Maximum size of a 64-bit signed integer -define(MAX_INT64, (1 bsl 63 - 1)). -define(PAX_GNU_SPARSE_NUMBLOCKS, <<"GNU.sparse.numblocks">>). -define(PAX_GNU_SPARSE_OFFSET, <<"GNU.sparse.offset">>). -define(PAX_GNU_SPARSE_NUMBYTES, <<"GNU.sparse.numbytes">>). -define(PAX_GNU_SPARSE_MAP, <<"GNU.sparse.map">>). -define(PAX_GNU_SPARSE_NAME, <<"GNU.sparse.name">>). -define(PAX_GNU_SPARSE_MAJOR, <<"GNU.sparse.major">>). -define(PAX_GNU_SPARSE_MINOR, <<"GNU.sparse.minor">>). -define(PAX_GNU_SPARSE_SIZE, <<"GNU.sparse.size">>). -define(PAX_GNU_SPARSE_REALSIZE, <<"GNU.sparse.realsize">>). -define(V7_NAME, 0). -define(V7_NAME_LEN, 100). -define(V7_MODE, 100). -define(V7_MODE_LEN, 8). -define(V7_UID, 108). -define(V7_UID_LEN, 8). -define(V7_GID, 116). -define(V7_GID_LEN, 8). -define(V7_SIZE, 124). -define(V7_SIZE_LEN, 12). -define(V7_MTIME, 136). -define(V7_MTIME_LEN, 12). -define(V7_CHKSUM, 148). -define(V7_CHKSUM_LEN, 8). -define(V7_TYPE, 156). -define(V7_TYPE_LEN, 1). -define(V7_LINKNAME, 157). -define(V7_LINKNAME_LEN, 100). -define(STAR_TRAILER, 508). -define(STAR_TRAILER_LEN, 4). -define(USTAR_MAGIC, 257). -define(USTAR_MAGIC_LEN, 6). -define(USTAR_VERSION, 263). -define(USTAR_VERSION_LEN, 2). -define(USTAR_UNAME, 265). -define(USTAR_UNAME_LEN, 32). -define(USTAR_GNAME, 297). -define(USTAR_GNAME_LEN, 32). -define(USTAR_DEVMAJ, 329). -define(USTAR_DEVMAJ_LEN, 8). -define(USTAR_DEVMIN, 337). -define(USTAR_DEVMIN_LEN, 8). -define(USTAR_PREFIX, 345). -define(USTAR_PREFIX_LEN, 155). -define(GNU_MAGIC, 257). -define(GNU_MAGIC_LEN, 6). -define(GNU_VERSION, 263). -define(GNU_VERSION_LEN, 2). %% ?BLOCK_SIZE of zero-bytes. %% Two of these in a row mark the end of an archive. -define(ZERO_BLOCK, <<0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0>>). -define(BILLION, 1000000000). -define(EPOCH, {{1970,1,1}, {0,0,0}}).