3 /// API for reading and writing sequentially from compressed
7 Copyright (C) 2007-2008, Chris Frey <cdfrey@foursquare.net>
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
18 See the GNU General Public License in the COPYING file at the
19 root directory of this project for more details.
30 TarFile::TarFile(const char *filename
,
32 tartype_t
*compress_ops
,
35 m_throw(always_throw
),
38 // figure out how to handle the file flags/modes
40 mode_t mode
= S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IROTH
;
43 flags
= O_WRONLY
| O_CREAT
| O_EXCL
;
49 // open... throw on error, as we are in the constructor
50 if( tar_open(&m_tar
, const_cast<char*>(filename
),
51 compress_ops
, flags
, mode
, TAR_VERBOSE
| TAR_GNU
) == -1 ) {
52 throw TarError(std::string("Unable to open tar file: ") + strerror(errno
));
60 } catch( TarError
&te
) {}
63 bool TarFile::False(const char *msg
)
72 bool TarFile::False(const std::string
&msg
, int err
)
74 std::string str
= msg
;
84 if( tar_append_eof(m_tar
) != 0 )
85 return False("Unable to write eof", errno
);
88 if( tar_close(m_tar
) != 0 ) {
89 return False("Unable to close file", errno
);
96 /// Appends a new file to the current tarfile, using tarpath as
97 /// its internal filename, and data as the complete file contents.
98 /// Uses current date and time as file mtime.
99 bool TarFile::AppendFile(const char *tarpath
, const std::string
&data
)
101 // write standard file header
102 th_set_type(m_tar
, REGTYPE
);
103 th_set_mode(m_tar
, 0644);
104 th_set_path(m_tar
, const_cast<char*>(tarpath
));
105 th_set_user(m_tar
, 0);
106 th_set_group(m_tar
, 0);
107 th_set_size(m_tar
, data
.size());
108 th_set_mtime(m_tar
, time(NULL
));
109 if( th_write(m_tar
) != 0 ) {
110 return False("Unable to write tar header", errno
);
113 // write the data in blocks until finished
114 char block
[T_BLOCKSIZE
];
115 for( size_t pos
= 0; pos
< data
.size(); pos
+= T_BLOCKSIZE
) {
116 memset(block
, 0, T_BLOCKSIZE
);
118 size_t size
= T_BLOCKSIZE
;
119 if( data
.size() - pos
< T_BLOCKSIZE
)
120 size
= data
.size() - pos
;
122 memcpy(block
, data
.data() + pos
, size
);
124 if( tar_block_write(m_tar
, block
) != T_BLOCKSIZE
) {
125 return False("Unable to write block", errno
);
132 /// Reads next available file into data, filling tarpath with
133 /// internal filename from tarball.
134 bool TarFile::ReadNextFile(std::string
&tarpath
, std::string
&data
)
140 // read next tar file header
141 if( th_read(m_tar
) != 0 ) {
142 // this is not necessarily an error, as it could just
143 // be the end of file, so a simple false is good here,
144 // don't throw an exception
149 // write standard file header
150 if( !TH_ISREG(m_tar
) ) {
151 return False("Only regular files are supported inside a tarball.");
154 tarpath
= th_get_pathname(m_tar
);
155 size_t size
= th_get_size(m_tar
);
157 // read the data in blocks until finished
158 char block
[T_BLOCKSIZE
];
159 for( size_t pos
= 0; pos
< size
; pos
+= T_BLOCKSIZE
) {
160 memset(block
, 0, T_BLOCKSIZE
);
162 size_t readsize
= T_BLOCKSIZE
;
163 if( size
- pos
< T_BLOCKSIZE
)
164 readsize
= size
- pos
;
166 if( tar_block_read(m_tar
, block
) != T_BLOCKSIZE
) {
167 return False("Unable to read block", errno
);
170 data
.append(block
, readsize
);
176 /// Read next available filename, skipping the data if it is
178 bool TarFile::ReadNextFilenameOnly(std::string
&tarpath
)
183 // read next tar file header
184 if( th_read(m_tar
) != 0 ) {
185 // this is not necessarily an error, as it could just
186 // be the end of file, so a simple false is good here,
187 // don't throw an exception
192 // write standard file header
193 if( !TH_ISREG(m_tar
) ) {
194 return False("Only regular files are supported inside a tarball.");
197 tarpath
= th_get_pathname(m_tar
);
199 if( tar_skip_regfile(m_tar
) != 0 ) {
200 return False("Unable to skip tar file", errno
);
220 cout
<< "Writing test file..." << endl
;
221 reuse::TarFile
output("tartest.tar.gz", true, true, true);
223 for( int i
= 0; i
< 60; i
++ ) {
224 data
.append("0123456789", 10);
227 output
.AppendFile("path1/test1.txt", data
);
228 output
.AppendFile("path2/test2.txt", data
);
232 cout
<< "Reading test file..." << endl
;
233 reuse::TarFile
input("tartest.tar.gz", false, true, true);
234 std::string path
, incoming
;
236 while( input
.ReadNextFile(path
, incoming
) ) {
237 cout
<< "Read: " << path
239 << (( data
== incoming
) ? "equal" : "different")
245 unlink("tartest.tar.gz");
247 } catch( reuse::TarFile::TarError
&te
) {
248 cerr
<< te
.what() << endl
;