Skip to main content

relibc/header/tar/
mod.rs

1//! `tar.h` implementation.
2//!
3//! See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/tar.h.html>.
4
5use core::slice;
6
7/// Block size for tar archives (512 bytes).
8pub const BLOCKSIZE: usize = 512;
9
10/// Default record size for tar archives (10KB, consisting of 20 blocks).
11pub const RECORDSIZE: usize = BLOCKSIZE * 20; // 10KB (default for tar archives)
12
13/// Field lengths in tar headers
14pub const NAME_SIZE: usize = 100; // File name
15pub const MODE_SIZE: usize = 8; // File mode
16pub const UID_SIZE: usize = 8; // Owner's numeric user ID
17pub const GID_SIZE: usize = 8; // Group's numeric user ID
18pub const SIZE_SIZE: usize = 12; // File size in bytes
19pub const MTIME_SIZE: usize = 12; // Modification time
20pub const CHKSUM_SIZE: usize = 8; // Checksum
21pub const LINKNAME_SIZE: usize = 100; // Name of linked file
22pub const MAGIC_SIZE: usize = 6; // Magic string size
23pub const VERSION_SIZE: usize = 2; // Version string size
24pub const UNAME_SIZE: usize = 32; // Owner user name
25pub const GNAME_SIZE: usize = 32; // Owner group name
26pub const DEVMAJOR_SIZE: usize = 8; // Major device number
27pub const DEVMINOR_SIZE: usize = 8; // Minor device number
28pub const PREFIX_SIZE: usize = 155; // Prefix for file name
29pub const HEADER_SIZE: usize = 512; // Total header size
30
31/// Bits used in the mode field - value in octal
32pub const TSUID: u16 = 0o4000; // Set user ID on execution
33pub const TSGID: u16 = 0o2000; // Set group ID on execution
34pub const TSVTX: u16 = 0o1000; // Sticky bit
35pub const TUREAD: u16 = 0o0400; // Read permission, owner
36pub const TUWRITE: u16 = 0o0200; // Write permission, owner
37pub const TUEXEC: u16 = 0o0100; // Execute/search permission, owner
38pub const TGREAD: u16 = 0o0040; // Read permission, group
39pub const TGWRITE: u16 = 0o0020; // Write permission, group
40pub const TGEXEC: u16 = 0o0010; // Execute/search permission, group
41pub const TOREAD: u16 = 0o0004; // Read permission, others
42pub const TOWRITE: u16 = 0o0002; // Write permission, others
43pub const TOEXEC: u16 = 0o0001; // Execute/search permission, others
44
45/// Values used in typeflag field
46pub const REGTYPE: u8 = b'0'; // Regular file
47pub const AREGTYPE: u8 = b'\0'; // Regular file (old format)
48pub const LNKTYPE: u8 = b'1'; // Link
49pub const SYMTYPE: u8 = b'2'; // Symbolic link
50pub const CHRTYPE: u8 = b'3'; // Character special
51pub const BLKTYPE: u8 = b'4'; // Block special
52pub const DIRTYPE: u8 = b'5'; // Directory
53pub const FIFOTYPE: u8 = b'6'; // FIFO special
54pub const CONTTYPE: u8 = b'7'; // Contiguous file
55
56/// tar format magic and version
57/// cbindgen:ignore
58pub const TMAGIC: &str = "ustar"; // Magic string : ustar and a null
59pub const TMAGLEN: usize = 6; // Length of the magic string
60/// cbindgen:ignore
61pub const TVERSION: &str = "00"; // Version string
62pub const TVERSLEN: usize = 2; // Length of the version string
63
64/// Reserved for future standards
65pub const XHDRTYPE: u8 = b'x'; // Extended header referring to the next file in the archive
66pub const XGLTYPE: u8 = b'g'; // Global extended header
67
68// Reserved values for GNU tar extensions
69// pub const GNUTYPE_DUMPDIR: u8 = b'D'; // Directory dump
70// pub const GNUTYPE_MULTIVOL: u8 = b'M'; // Multi-volume file
71// pub const GNUTYPE_LONGNAME: u8 = b'L'; // Long file name
72// pub const GNUTYPE_LONGLINK: u8 = b'K'; // Long link name
73// pub const GNUTYPE_SPARSE: u8 = b'S'; // Sparse file
74
75/// Represents a tar archive header following the POSIX ustar format.
76///
77/// The header contains metadata about a file in a tar archive, including
78/// its name, size, permissions, and other attributes. All text fields are
79/// null-terminated.
80#[repr(C)]
81#[derive(Debug, Clone, Copy)]
82pub struct TarHeader {
83    // Byte offset - usage
84    pub name: [u8; NAME_SIZE],         // 0   - File name
85    pub mode: [u8; MODE_SIZE],         // 100 - Permissions
86    pub uid: [u8; UID_SIZE],           // 108 - User ID
87    pub gid: [u8; GID_SIZE],           // 116 - Group ID
88    pub size: [u8; SIZE_SIZE],         // 124 - File size in bytes
89    pub mtime: [u8; MTIME_SIZE],       // 136 - Modification time
90    pub chksum: [u8; CHKSUM_SIZE],     // 148 - Header checksum
91    pub typeflag: u8,                  // 156 - File type
92    pub linkname: [u8; LINKNAME_SIZE], // 157 - Linked file name
93    pub magic: [u8; MAGIC_SIZE],       // 257 - UStar magic
94    pub version: [u8; VERSION_SIZE],   // 263 - UStar version
95    pub uname: [u8; UNAME_SIZE],       // 265 - Owner user name
96    pub gname: [u8; GNAME_SIZE],       // 297 - Owner group name
97    pub devmajor: [u8; DEVMAJOR_SIZE], // 329 - Major device number
98    pub devminor: [u8; DEVMINOR_SIZE], // 337 - Minor device number
99    pub prefix: [u8; PREFIX_SIZE],     // 345 - Prefix for file name
100    pub padding: [u8; 12],             // 500 - Padding to make 512 bytes
101}
102
103impl Default for TarHeader {
104    fn default() -> Self {
105        let mut header = Self {
106            name: [0; NAME_SIZE],
107            mode: [0; MODE_SIZE],
108            uid: [0; UID_SIZE],
109            gid: [0; GID_SIZE],
110            size: [0; SIZE_SIZE],
111            mtime: [0; MTIME_SIZE],
112            chksum: [0; CHKSUM_SIZE],
113            typeflag: AREGTYPE,
114            linkname: [0; LINKNAME_SIZE],
115            magic: [0; MAGIC_SIZE],
116            version: [0; VERSION_SIZE],
117            uname: [0; UNAME_SIZE],
118            gname: [0; GNAME_SIZE],
119            devmajor: [0; DEVMAJOR_SIZE],
120            devminor: [0; DEVMINOR_SIZE],
121            prefix: [0; PREFIX_SIZE],
122            padding: [0; 12],
123        };
124
125        // Set default magic ("ustar") and version ("00")
126        let magic_bytes = TMAGIC.as_bytes(); // "ustar"
127        header.magic[..magic_bytes.len()].copy_from_slice(magic_bytes);
128        // tar specification often expects "ustar\0"
129        if MAGIC_SIZE >= 6 && TMAGIC.len() < MAGIC_SIZE {
130            header.magic[TMAGIC.len()] = 0;
131        }
132
133        let version_bytes = TVERSION.as_bytes(); // "00"
134        header.version[..version_bytes.len()].copy_from_slice(version_bytes);
135
136        header
137    }
138}
139
140impl TarHeader {
141    /// Calculates the checksum of the tar header as required by the specification.
142    /// Before computing, the checksum field is treated as if it contained all spaces (0x20).
143    pub fn calculate_checksum(&self) -> usize {
144        let mut header_copy = *self;
145        header_copy.chksum.fill(b' ');
146        let bytes =
147            unsafe { slice::from_raw_parts((&raw const header_copy).cast::<u8>(), HEADER_SIZE) };
148        bytes.iter().map(|&b| b as usize).sum()
149    }
150}