]> git.saurik.com Git - apt.git/blame - apt-pkg/contrib/fileutl.cc
use liblzma-dev to provide xz/lzma support
[apt.git] / apt-pkg / contrib / fileutl.cc
CommitLineData
578bfd0a
AL
1// -*- mode: cpp; mode: fold -*-
2// Description /*{{{*/
7da2b375 3// $Id: fileutl.cc,v 1.42 2002/09/14 05:29:22 jgg Exp $
578bfd0a
AL
4/* ######################################################################
5
6 File Utilities
7
8 CopyFile - Buffered copy of a single file
9 GetLock - dpkg compatible lock file manipulation (fcntl)
10
614adaa0
MV
11 Most of this source is placed in the Public Domain, do with it what
12 you will
7da2b375 13 It was originally written by Jason Gunthorpe <jgg@debian.org>.
a3a03f5d 14 FileFd gzip support added by Martin Pitt <martin.pitt@canonical.com>
578bfd0a 15
614adaa0
MV
16 The exception is RunScripts() it is under the GPLv2
17
578bfd0a
AL
18 ##################################################################### */
19 /*}}}*/
20// Include Files /*{{{*/
ea542140
DK
21#include <config.h>
22
094a497d 23#include <apt-pkg/fileutl.h>
1cd1c398 24#include <apt-pkg/strutl.h>
094a497d 25#include <apt-pkg/error.h>
b2e465d6 26#include <apt-pkg/sptr.h>
468720c5 27#include <apt-pkg/aptconfiguration.h>
75ef8f14 28#include <apt-pkg/configuration.h>
453b82a3 29#include <apt-pkg/macros.h>
b2e465d6 30
453b82a3
DK
31#include <ctype.h>
32#include <stdarg.h>
33#include <stddef.h>
34#include <sys/select.h>
35#include <time.h>
36#include <string>
37#include <vector>
152ab79e 38#include <cstdlib>
4f333a8b 39#include <cstring>
3010fb0e 40#include <cstdio>
4d055c05 41#include <iostream>
578bfd0a 42#include <unistd.h>
2c206aa4 43#include <fcntl.h>
578bfd0a 44#include <sys/stat.h>
cc2313b7 45#include <sys/time.h>
1ae93c94 46#include <sys/wait.h>
46e39c8e 47#include <dirent.h>
54676e1a 48#include <signal.h>
65a1e968 49#include <errno.h>
488011fa
MV
50#include <glob.h>
51
75ef8f14 52#include <set>
46e39c8e 53#include <algorithm>
2cae0ccb 54
7efb8c8e
DK
55#ifdef HAVE_ZLIB
56 #include <zlib.h>
699b209e 57#endif
c4997486
DK
58#ifdef HAVE_BZ2
59 #include <bzlib.h>
60#endif
7f350a37
DK
61#ifdef HAVE_LZMA
62 #include <lzma.h>
63#endif
032bd56f 64
2a79d5b5 65#ifdef WORDS_BIGENDIAN
2cae0ccb
DK
66#include <inttypes.h>
67#endif
ea542140
DK
68
69#include <apti18n.h>
578bfd0a
AL
70 /*}}}*/
71
4d055c05
AL
72using namespace std;
73
032bd56f
DK
74class FileFdPrivate {
75 public:
7efb8c8e 76#ifdef HAVE_ZLIB
032bd56f 77 gzFile gz;
c4997486
DK
78#endif
79#ifdef HAVE_BZ2
80 BZFILE* bz2;
7f350a37
DK
81#endif
82#ifdef HAVE_LZMA
83 struct LZMAFILE {
84 FILE* file;
85 uint8_t buffer[4096];
86 lzma_stream stream;
87 lzma_ret err;
88 bool eof;
89 bool compressing;
90
91 LZMAFILE() : file(NULL), eof(false), compressing(false) {}
92 ~LZMAFILE() {
93 if (compressing == true)
94 {
95 for (;;) {
96 stream.avail_out = sizeof(buffer)/sizeof(buffer[0]);
97 stream.next_out = buffer;
98 err = lzma_code(&stream, LZMA_FINISH);
99 if (err != LZMA_OK && err != LZMA_STREAM_END)
100 {
101 _error->Error("~LZMAFILE: Compress finalisation failed");
102 break;
103 }
104 size_t const n = sizeof(buffer)/sizeof(buffer[0]) - stream.avail_out;
105 if (n && fwrite(buffer, 1, n, file) != n)
106 {
107 _error->Errno("~LZMAFILE",_("Write error"));
108 break;
109 }
110 if (err == LZMA_STREAM_END)
111 break;
112 }
113 }
114 lzma_end(&stream);
115 fclose(file);
116 }
117 };
118 LZMAFILE* lzma;
699b209e 119#endif
561f860a 120 int compressed_fd;
699b209e
DK
121 pid_t compressor_pid;
122 bool pipe;
123 APT::Configuration::Compressor compressor;
52b47296 124 unsigned int openmode;
1abbc47c 125 unsigned long long seekpos;
7f350a37
DK
126 FileFdPrivate() :
127#ifdef HAVE_ZLIB
128 gz(NULL),
129#endif
130#ifdef HAVE_BZ2
131 bz2(NULL),
132#endif
133#ifdef HAVE_LZMA
134 lzma(NULL),
135#endif
c4997486 136 compressed_fd(-1), compressor_pid(-1), pipe(false),
1abbc47c 137 openmode(0), seekpos(0) {};
500400fe
DK
138 bool CloseDown(std::string const &FileName)
139 {
140 bool Res = true;
141#ifdef HAVE_ZLIB
142 if (gz != NULL) {
143 int const e = gzclose(gz);
144 gz = NULL;
145 // gzdclose() on empty files always fails with "buffer error" here, ignore that
146 if (e != 0 && e != Z_BUF_ERROR)
147 Res &= _error->Errno("close",_("Problem closing the gzip file %s"), FileName.c_str());
148 }
149#endif
150#ifdef HAVE_BZ2
151 if (bz2 != NULL) {
152 BZ2_bzclose(bz2);
153 bz2 = NULL;
154 }
7f350a37
DK
155#endif
156#ifdef HAVE_LZMA
157 if (lzma != NULL) {
158 delete lzma;
159 lzma = NULL;
160 }
500400fe
DK
161#endif
162 if (compressor_pid > 0)
163 ExecWait(compressor_pid, "FileFdCompressor", true);
164 compressor_pid = -1;
165
166 return Res;
167 }
168 ~FileFdPrivate() { CloseDown(""); }
032bd56f
DK
169};
170
614adaa0
MV
171// RunScripts - Run a set of scripts from a configuration subtree /*{{{*/
172// ---------------------------------------------------------------------
173/* */
174bool RunScripts(const char *Cnf)
175{
176 Configuration::Item const *Opts = _config->Tree(Cnf);
177 if (Opts == 0 || Opts->Child == 0)
178 return true;
179 Opts = Opts->Child;
180
181 // Fork for running the system calls
182 pid_t Child = ExecFork();
183
184 // This is the child
185 if (Child == 0)
186 {
cfba4f69
MV
187 if (_config->FindDir("DPkg::Chroot-Directory","/") != "/")
188 {
189 std::cerr << "Chrooting into "
190 << _config->FindDir("DPkg::Chroot-Directory")
191 << std::endl;
192 if (chroot(_config->FindDir("DPkg::Chroot-Directory","/").c_str()) != 0)
193 _exit(100);
194 }
195
614adaa0
MV
196 if (chdir("/tmp/") != 0)
197 _exit(100);
198
199 unsigned int Count = 1;
200 for (; Opts != 0; Opts = Opts->Next, Count++)
201 {
202 if (Opts->Value.empty() == true)
203 continue;
204
205 if (system(Opts->Value.c_str()) != 0)
206 _exit(100+Count);
207 }
208 _exit(0);
209 }
210
211 // Wait for the child
212 int Status = 0;
213 while (waitpid(Child,&Status,0) != Child)
214 {
215 if (errno == EINTR)
216 continue;
217 return _error->Errno("waitpid","Couldn't wait for subprocess");
218 }
219
220 // Restore sig int/quit
221 signal(SIGQUIT,SIG_DFL);
222 signal(SIGINT,SIG_DFL);
223
224 // Check for an error code.
225 if (WIFEXITED(Status) == 0 || WEXITSTATUS(Status) != 0)
226 {
227 unsigned int Count = WEXITSTATUS(Status);
228 if (Count > 100)
229 {
230 Count -= 100;
231 for (; Opts != 0 && Count != 1; Opts = Opts->Next, Count--);
232 _error->Error("Problem executing scripts %s '%s'",Cnf,Opts->Value.c_str());
233 }
234
235 return _error->Error("Sub-process returned an error code");
236 }
237
238 return true;
239}
240 /*}}}*/
241
578bfd0a
AL
242// CopyFile - Buffered copy of a file /*{{{*/
243// ---------------------------------------------------------------------
244/* The caller is expected to set things so that failure causes erasure */
8b89e57f 245bool CopyFile(FileFd &From,FileFd &To)
578bfd0a 246{
2128d3fc
DK
247 if (From.IsOpen() == false || To.IsOpen() == false ||
248 From.Failed() == true || To.Failed() == true)
578bfd0a
AL
249 return false;
250
251 // Buffered copy between fds
b2e465d6 252 SPtrArray<unsigned char> Buf = new unsigned char[64000];
650faab0 253 unsigned long long Size = From.Size();
b0db36b1 254 while (Size != 0)
578bfd0a 255 {
650faab0 256 unsigned long long ToRead = Size;
b0db36b1
AL
257 if (Size > 64000)
258 ToRead = 64000;
259
4a6d5862 260 if (From.Read(Buf,ToRead) == false ||
b0db36b1 261 To.Write(Buf,ToRead) == false)
578bfd0a 262 return false;
b0db36b1
AL
263
264 Size -= ToRead;
578bfd0a
AL
265 }
266
578bfd0a
AL
267 return true;
268}
269 /*}}}*/
270// GetLock - Gets a lock file /*{{{*/
271// ---------------------------------------------------------------------
272/* This will create an empty file of the given name and lock it. Once this
273 is done all other calls to GetLock in any other process will fail with
274 -1. The return result is the fd of the file, the call should call
275 close at some time. */
276int GetLock(string File,bool Errors)
277{
f659b39a
OS
278 // GetLock() is used in aptitude on directories with public-write access
279 // Use O_NOFOLLOW here to prevent symlink traversal attacks
280 int FD = open(File.c_str(),O_RDWR | O_CREAT | O_NOFOLLOW,0640);
578bfd0a
AL
281 if (FD < 0)
282 {
1e3f4083 283 // Read only .. can't have locking problems there.
b2e465d6
AL
284 if (errno == EROFS)
285 {
286 _error->Warning(_("Not using locking for read only lock file %s"),File.c_str());
287 return dup(0); // Need something for the caller to close
288 }
289
578bfd0a 290 if (Errors == true)
b2e465d6
AL
291 _error->Errno("open",_("Could not open lock file %s"),File.c_str());
292
293 // Feh.. We do this to distinguish the lock vs open case..
294 errno = EPERM;
578bfd0a
AL
295 return -1;
296 }
b2e465d6
AL
297 SetCloseExec(FD,true);
298
1e3f4083 299 // Acquire a write lock
578bfd0a 300 struct flock fl;
c71bc556
AL
301 fl.l_type = F_WRLCK;
302 fl.l_whence = SEEK_SET;
303 fl.l_start = 0;
304 fl.l_len = 0;
578bfd0a
AL
305 if (fcntl(FD,F_SETLK,&fl) == -1)
306 {
3d165906
MV
307 // always close to not leak resources
308 int Tmp = errno;
309 close(FD);
310 errno = Tmp;
311
d89df07a
AL
312 if (errno == ENOLCK)
313 {
b2e465d6
AL
314 _error->Warning(_("Not using locking for nfs mounted lock file %s"),File.c_str());
315 return dup(0); // Need something for the caller to close
3d165906
MV
316 }
317
578bfd0a 318 if (Errors == true)
b2e465d6
AL
319 _error->Errno("open",_("Could not get lock %s"),File.c_str());
320
578bfd0a
AL
321 return -1;
322 }
323
324 return FD;
325}
326 /*}}}*/
327// FileExists - Check if a file exists /*{{{*/
328// ---------------------------------------------------------------------
36f1098a 329/* Beware: Directories are also files! */
578bfd0a
AL
330bool FileExists(string File)
331{
332 struct stat Buf;
333 if (stat(File.c_str(),&Buf) != 0)
334 return false;
335 return true;
336}
337 /*}}}*/
36f1098a
DK
338// RealFileExists - Check if a file exists and if it is really a file /*{{{*/
339// ---------------------------------------------------------------------
340/* */
341bool RealFileExists(string File)
342{
343 struct stat Buf;
344 if (stat(File.c_str(),&Buf) != 0)
345 return false;
346 return ((Buf.st_mode & S_IFREG) != 0);
347}
348 /*}}}*/
1cd1c398
DK
349// DirectoryExists - Check if a directory exists and is really one /*{{{*/
350// ---------------------------------------------------------------------
351/* */
352bool DirectoryExists(string const &Path)
353{
354 struct stat Buf;
355 if (stat(Path.c_str(),&Buf) != 0)
356 return false;
357 return ((Buf.st_mode & S_IFDIR) != 0);
358}
359 /*}}}*/
360// CreateDirectory - poor man's mkdir -p guarded by a parent directory /*{{{*/
361// ---------------------------------------------------------------------
362/* This method will create all directories needed for path in good old
363 mkdir -p style but refuses to do this if Parent is not a prefix of
364 this Path. Example: /var/cache/ and /var/cache/apt/archives are given,
365 so it will create apt/archives if /var/cache exists - on the other
366 hand if the parent is /var/lib the creation will fail as this path
367 is not a parent of the path to be generated. */
368bool CreateDirectory(string const &Parent, string const &Path)
369{
370 if (Parent.empty() == true || Path.empty() == true)
371 return false;
372
373 if (DirectoryExists(Path) == true)
374 return true;
375
376 if (DirectoryExists(Parent) == false)
377 return false;
378
379 // we are not going to create directories "into the blue"
9ce3cfc9 380 if (Path.compare(0, Parent.length(), Parent) != 0)
1cd1c398
DK
381 return false;
382
383 vector<string> const dirs = VectorizeString(Path.substr(Parent.size()), '/');
384 string progress = Parent;
385 for (vector<string>::const_iterator d = dirs.begin(); d != dirs.end(); ++d)
386 {
387 if (d->empty() == true)
388 continue;
389
390 progress.append("/").append(*d);
391 if (DirectoryExists(progress) == true)
392 continue;
393
394 if (mkdir(progress.c_str(), 0755) != 0)
395 return false;
396 }
397 return true;
398}
399 /*}}}*/
7753e468 400// CreateAPTDirectoryIfNeeded - ensure that the given directory exists /*{{{*/
b29c3712
DK
401// ---------------------------------------------------------------------
402/* a small wrapper around CreateDirectory to check if it exists and to
403 remove the trailing "/apt/" from the parent directory if needed */
7753e468 404bool CreateAPTDirectoryIfNeeded(string const &Parent, string const &Path)
b29c3712
DK
405{
406 if (DirectoryExists(Path) == true)
407 return true;
408
409 size_t const len = Parent.size();
410 if (len > 5 && Parent.find("/apt/", len - 6, 5) == len - 5)
411 {
412 if (CreateDirectory(Parent.substr(0,len-5), Path) == true)
413 return true;
414 }
415 else if (CreateDirectory(Parent, Path) == true)
416 return true;
417
418 return false;
419}
420 /*}}}*/
46e39c8e
MV
421// GetListOfFilesInDir - returns a vector of files in the given dir /*{{{*/
422// ---------------------------------------------------------------------
423/* If an extension is given only files with this extension are included
424 in the returned vector, otherwise every "normal" file is included. */
b39c1859
MV
425std::vector<string> GetListOfFilesInDir(string const &Dir, string const &Ext,
426 bool const &SortList, bool const &AllowNoExt)
427{
428 std::vector<string> ext;
429 ext.reserve(2);
430 if (Ext.empty() == false)
431 ext.push_back(Ext);
432 if (AllowNoExt == true && ext.empty() == false)
433 ext.push_back("");
434 return GetListOfFilesInDir(Dir, ext, SortList);
435}
436std::vector<string> GetListOfFilesInDir(string const &Dir, std::vector<string> const &Ext,
437 bool const &SortList)
438{
439 // Attention debuggers: need to be set with the environment config file!
440 bool const Debug = _config->FindB("Debug::GetListOfFilesInDir", false);
441 if (Debug == true)
442 {
443 std::clog << "Accept in " << Dir << " only files with the following " << Ext.size() << " extensions:" << std::endl;
444 if (Ext.empty() == true)
445 std::clog << "\tNO extension" << std::endl;
446 else
447 for (std::vector<string>::const_iterator e = Ext.begin();
448 e != Ext.end(); ++e)
449 std::clog << '\t' << (e->empty() == true ? "NO" : *e) << " extension" << std::endl;
450 }
451
46e39c8e 452 std::vector<string> List;
36f1098a 453
69c2ecbd 454 if (DirectoryExists(Dir) == false)
36f1098a
DK
455 {
456 _error->Error(_("List of files can't be created as '%s' is not a directory"), Dir.c_str());
457 return List;
458 }
459
1408e219 460 Configuration::MatchAgainstConfig SilentIgnore("Dir::Ignore-Files-Silently");
46e39c8e
MV
461 DIR *D = opendir(Dir.c_str());
462 if (D == 0)
463 {
464 _error->Errno("opendir",_("Unable to read %s"),Dir.c_str());
465 return List;
466 }
467
468 for (struct dirent *Ent = readdir(D); Ent != 0; Ent = readdir(D))
469 {
b39c1859 470 // skip "hidden" files
46e39c8e
MV
471 if (Ent->d_name[0] == '.')
472 continue;
473
491058e3
DK
474 // Make sure it is a file and not something else
475 string const File = flCombine(Dir,Ent->d_name);
476#ifdef _DIRENT_HAVE_D_TYPE
477 if (Ent->d_type != DT_REG)
478#endif
479 {
69c2ecbd 480 if (RealFileExists(File) == false)
491058e3 481 {
84e254d6
DK
482 // do not show ignoration warnings for directories
483 if (
484#ifdef _DIRENT_HAVE_D_TYPE
485 Ent->d_type == DT_DIR ||
486#endif
69c2ecbd 487 DirectoryExists(File) == true)
84e254d6 488 continue;
491058e3
DK
489 if (SilentIgnore.Match(Ent->d_name) == false)
490 _error->Notice(_("Ignoring '%s' in directory '%s' as it is not a regular file"), Ent->d_name, Dir.c_str());
491 continue;
492 }
493 }
494
b39c1859
MV
495 // check for accepted extension:
496 // no extension given -> periods are bad as hell!
497 // extensions given -> "" extension allows no extension
498 if (Ext.empty() == false)
499 {
500 string d_ext = flExtension(Ent->d_name);
501 if (d_ext == Ent->d_name) // no extension
502 {
503 if (std::find(Ext.begin(), Ext.end(), "") == Ext.end())
504 {
505 if (Debug == true)
506 std::clog << "Bad file: " << Ent->d_name << " → no extension" << std::endl;
5edc3966 507 if (SilentIgnore.Match(Ent->d_name) == false)
491058e3 508 _error->Notice(_("Ignoring file '%s' in directory '%s' as it has no filename extension"), Ent->d_name, Dir.c_str());
b39c1859
MV
509 continue;
510 }
511 }
512 else if (std::find(Ext.begin(), Ext.end(), d_ext) == Ext.end())
513 {
514 if (Debug == true)
515 std::clog << "Bad file: " << Ent->d_name << " → bad extension »" << flExtension(Ent->d_name) << "«" << std::endl;
1408e219 516 if (SilentIgnore.Match(Ent->d_name) == false)
491058e3 517 _error->Notice(_("Ignoring file '%s' in directory '%s' as it has an invalid filename extension"), Ent->d_name, Dir.c_str());
b39c1859
MV
518 continue;
519 }
520 }
46e39c8e 521
b39c1859 522 // Skip bad filenames ala run-parts
46e39c8e
MV
523 const char *C = Ent->d_name;
524 for (; *C != 0; ++C)
525 if (isalpha(*C) == 0 && isdigit(*C) == 0
9d39208a 526 && *C != '_' && *C != '-' && *C != ':') {
b39c1859
MV
527 // no required extension -> dot is a bad character
528 if (*C == '.' && Ext.empty() == false)
529 continue;
46e39c8e 530 break;
b39c1859 531 }
46e39c8e 532
b39c1859 533 // we don't reach the end of the name -> bad character included
46e39c8e 534 if (*C != 0)
b39c1859
MV
535 {
536 if (Debug == true)
537 std::clog << "Bad file: " << Ent->d_name << " → bad character »"
538 << *C << "« in filename (period allowed: " << (Ext.empty() ? "no" : "yes") << ")" << std::endl;
539 continue;
540 }
541
fbb2c7e0
DK
542 // skip filenames which end with a period. These are never valid
543 if (*(C - 1) == '.')
544 {
545 if (Debug == true)
546 std::clog << "Bad file: " << Ent->d_name << " → Period as last character" << std::endl;
547 continue;
548 }
549
550 if (Debug == true)
551 std::clog << "Accept file: " << Ent->d_name << " in " << Dir << std::endl;
552 List.push_back(File);
553 }
554 closedir(D);
555
556 if (SortList == true)
557 std::sort(List.begin(),List.end());
558 return List;
559}
560std::vector<string> GetListOfFilesInDir(string const &Dir, bool SortList)
561{
562 bool const Debug = _config->FindB("Debug::GetListOfFilesInDir", false);
563 if (Debug == true)
564 std::clog << "Accept in " << Dir << " all regular files" << std::endl;
565
566 std::vector<string> List;
567
69c2ecbd 568 if (DirectoryExists(Dir) == false)
fbb2c7e0
DK
569 {
570 _error->Error(_("List of files can't be created as '%s' is not a directory"), Dir.c_str());
571 return List;
572 }
573
574 DIR *D = opendir(Dir.c_str());
575 if (D == 0)
576 {
577 _error->Errno("opendir",_("Unable to read %s"),Dir.c_str());
578 return List;
579 }
580
581 for (struct dirent *Ent = readdir(D); Ent != 0; Ent = readdir(D))
582 {
583 // skip "hidden" files
584 if (Ent->d_name[0] == '.')
585 continue;
586
587 // Make sure it is a file and not something else
588 string const File = flCombine(Dir,Ent->d_name);
589#ifdef _DIRENT_HAVE_D_TYPE
590 if (Ent->d_type != DT_REG)
591#endif
592 {
69c2ecbd 593 if (RealFileExists(File) == false)
fbb2c7e0
DK
594 {
595 if (Debug == true)
596 std::clog << "Bad file: " << Ent->d_name << " → it is not a real file" << std::endl;
597 continue;
598 }
599 }
600
601 // Skip bad filenames ala run-parts
602 const char *C = Ent->d_name;
603 for (; *C != 0; ++C)
604 if (isalpha(*C) == 0 && isdigit(*C) == 0
605 && *C != '_' && *C != '-' && *C != '.')
606 break;
607
608 // we don't reach the end of the name -> bad character included
609 if (*C != 0)
610 {
611 if (Debug == true)
612 std::clog << "Bad file: " << Ent->d_name << " → bad character »" << *C << "« in filename" << std::endl;
613 continue;
614 }
615
b39c1859
MV
616 // skip filenames which end with a period. These are never valid
617 if (*(C - 1) == '.')
618 {
619 if (Debug == true)
620 std::clog << "Bad file: " << Ent->d_name << " → Period as last character" << std::endl;
46e39c8e 621 continue;
b39c1859 622 }
46e39c8e 623
b39c1859
MV
624 if (Debug == true)
625 std::clog << "Accept file: " << Ent->d_name << " in " << Dir << std::endl;
46e39c8e
MV
626 List.push_back(File);
627 }
628 closedir(D);
629
630 if (SortList == true)
631 std::sort(List.begin(),List.end());
632 return List;
633}
634 /*}}}*/
578bfd0a
AL
635// SafeGetCWD - This is a safer getcwd that returns a dynamic string /*{{{*/
636// ---------------------------------------------------------------------
637/* We return / on failure. */
638string SafeGetCWD()
639{
640 // Stash the current dir.
641 char S[300];
642 S[0] = 0;
7f25bdff 643 if (getcwd(S,sizeof(S)-2) == 0)
578bfd0a 644 return "/";
7f25bdff
AL
645 unsigned int Len = strlen(S);
646 S[Len] = '/';
647 S[Len+1] = 0;
578bfd0a
AL
648 return S;
649}
650 /*}}}*/
2ec858bc
MV
651// GetModificationTime - Get the mtime of the given file or -1 on error /*{{{*/
652// ---------------------------------------------------------------------
653/* We return / on failure. */
654time_t GetModificationTime(string const &Path)
655{
656 struct stat St;
657 if (stat(Path.c_str(), &St) < 0)
658 return -1;
659 return St.st_mtime;
660}
661 /*}}}*/
8ce4327b
AL
662// flNotDir - Strip the directory from the filename /*{{{*/
663// ---------------------------------------------------------------------
664/* */
665string flNotDir(string File)
666{
667 string::size_type Res = File.rfind('/');
668 if (Res == string::npos)
669 return File;
670 Res++;
671 return string(File,Res,Res - File.length());
672}
673 /*}}}*/
d38b7b3d
AL
674// flNotFile - Strip the file from the directory name /*{{{*/
675// ---------------------------------------------------------------------
171c45bc 676/* Result ends in a / */
d38b7b3d
AL
677string flNotFile(string File)
678{
679 string::size_type Res = File.rfind('/');
680 if (Res == string::npos)
171c45bc 681 return "./";
d38b7b3d
AL
682 Res++;
683 return string(File,0,Res);
684}
685 /*}}}*/
b2e465d6
AL
686// flExtension - Return the extension for the file /*{{{*/
687// ---------------------------------------------------------------------
688/* */
689string flExtension(string File)
690{
691 string::size_type Res = File.rfind('.');
692 if (Res == string::npos)
693 return File;
694 Res++;
695 return string(File,Res,Res - File.length());
696}
697 /*}}}*/
421c8d10
AL
698// flNoLink - If file is a symlink then deref it /*{{{*/
699// ---------------------------------------------------------------------
700/* If the name is not a link then the returned path is the input. */
701string flNoLink(string File)
702{
703 struct stat St;
704 if (lstat(File.c_str(),&St) != 0 || S_ISLNK(St.st_mode) == 0)
705 return File;
706 if (stat(File.c_str(),&St) != 0)
707 return File;
708
709 /* Loop resolving the link. There is no need to limit the number of
710 loops because the stat call above ensures that the symlink is not
711 circular */
712 char Buffer[1024];
713 string NFile = File;
714 while (1)
715 {
716 // Read the link
3286ad13 717 ssize_t Res;
421c8d10 718 if ((Res = readlink(NFile.c_str(),Buffer,sizeof(Buffer))) <= 0 ||
3286ad13 719 (size_t)Res >= sizeof(Buffer))
421c8d10
AL
720 return File;
721
722 // Append or replace the previous path
723 Buffer[Res] = 0;
724 if (Buffer[0] == '/')
725 NFile = Buffer;
726 else
727 NFile = flNotFile(NFile) + Buffer;
728
729 // See if we are done
730 if (lstat(NFile.c_str(),&St) != 0)
731 return File;
732 if (S_ISLNK(St.st_mode) == 0)
733 return NFile;
734 }
735}
736 /*}}}*/
b2e465d6
AL
737// flCombine - Combine a file and a directory /*{{{*/
738// ---------------------------------------------------------------------
739/* If the file is an absolute path then it is just returned, otherwise
740 the directory is pre-pended to it. */
741string flCombine(string Dir,string File)
742{
743 if (File.empty() == true)
744 return string();
745
746 if (File[0] == '/' || Dir.empty() == true)
747 return File;
748 if (File.length() >= 2 && File[0] == '.' && File[1] == '/')
749 return File;
750 if (Dir[Dir.length()-1] == '/')
751 return Dir + File;
752 return Dir + '/' + File;
753}
754 /*}}}*/
3b5421b4
AL
755// SetCloseExec - Set the close on exec flag /*{{{*/
756// ---------------------------------------------------------------------
757/* */
758void SetCloseExec(int Fd,bool Close)
759{
760 if (fcntl(Fd,F_SETFD,(Close == false)?0:FD_CLOEXEC) != 0)
761 {
762 cerr << "FATAL -> Could not set close on exec " << strerror(errno) << endl;
763 exit(100);
764 }
765}
766 /*}}}*/
767// SetNonBlock - Set the nonblocking flag /*{{{*/
768// ---------------------------------------------------------------------
769/* */
770void SetNonBlock(int Fd,bool Block)
771{
0a8a80e5
AL
772 int Flags = fcntl(Fd,F_GETFL) & (~O_NONBLOCK);
773 if (fcntl(Fd,F_SETFL,Flags | ((Block == false)?0:O_NONBLOCK)) != 0)
3b5421b4
AL
774 {
775 cerr << "FATAL -> Could not set non-blocking flag " << strerror(errno) << endl;
776 exit(100);
777 }
778}
779 /*}}}*/
780// WaitFd - Wait for a FD to become readable /*{{{*/
781// ---------------------------------------------------------------------
b2e465d6 782/* This waits for a FD to become readable using select. It is useful for
6d5dd02a
AL
783 applications making use of non-blocking sockets. The timeout is
784 in seconds. */
1084d58a 785bool WaitFd(int Fd,bool write,unsigned long timeout)
3b5421b4
AL
786{
787 fd_set Set;
cc2313b7 788 struct timeval tv;
3b5421b4
AL
789 FD_ZERO(&Set);
790 FD_SET(Fd,&Set);
6d5dd02a
AL
791 tv.tv_sec = timeout;
792 tv.tv_usec = 0;
1084d58a 793 if (write == true)
b0db36b1
AL
794 {
795 int Res;
796 do
797 {
798 Res = select(Fd+1,0,&Set,0,(timeout != 0?&tv:0));
799 }
800 while (Res < 0 && errno == EINTR);
801
802 if (Res <= 0)
803 return false;
1084d58a
AL
804 }
805 else
806 {
b0db36b1
AL
807 int Res;
808 do
809 {
810 Res = select(Fd+1,&Set,0,0,(timeout != 0?&tv:0));
811 }
812 while (Res < 0 && errno == EINTR);
813
814 if (Res <= 0)
815 return false;
cc2313b7 816 }
1084d58a 817
3b5421b4
AL
818 return true;
819}
820 /*}}}*/
96ae6de5 821// MergeKeepFdsFromConfiguration - Merge APT::Keep-Fds configuration /*{{{*/
54676e1a 822// ---------------------------------------------------------------------
96ae6de5
MV
823/* This is used to merge the APT::Keep-Fds with the provided KeepFDs
824 * set.
825 */
826void MergeKeepFdsFromConfiguration(std::set<int> &KeepFDs)
e45c4617 827{
e45c4617
MV
828 Configuration::Item const *Opts = _config->Tree("APT::Keep-Fds");
829 if (Opts != 0 && Opts->Child != 0)
830 {
831 Opts = Opts->Child;
832 for (; Opts != 0; Opts = Opts->Next)
833 {
834 if (Opts->Value.empty() == true)
835 continue;
836 int fd = atoi(Opts->Value.c_str());
837 KeepFDs.insert(fd);
838 }
839 }
96ae6de5
MV
840}
841 /*}}}*/
842// ExecFork - Magical fork that sanitizes the context before execing /*{{{*/
843// ---------------------------------------------------------------------
844/* This is used if you want to cleanse the environment for the forked
845 child, it fixes up the important signals and nukes all of the fds,
846 otherwise acts like normal fork. */
847pid_t ExecFork()
848{
849 set<int> KeepFDs;
850 // we need to merge the Keep-Fds as external tools like
851 // debconf-apt-progress use it
852 MergeKeepFdsFromConfiguration(KeepFDs);
e45c4617
MV
853 return ExecFork(KeepFDs);
854}
855
856pid_t ExecFork(std::set<int> KeepFDs)
54676e1a
AL
857{
858 // Fork off the process
859 pid_t Process = fork();
860 if (Process < 0)
861 {
862 cerr << "FATAL -> Failed to fork." << endl;
863 exit(100);
864 }
865
866 // Spawn the subprocess
867 if (Process == 0)
868 {
869 // Setup the signals
870 signal(SIGPIPE,SIG_DFL);
871 signal(SIGQUIT,SIG_DFL);
872 signal(SIGINT,SIG_DFL);
873 signal(SIGWINCH,SIG_DFL);
874 signal(SIGCONT,SIG_DFL);
875 signal(SIGTSTP,SIG_DFL);
75ef8f14 876
54676e1a 877 // Close all of our FDs - just in case
61f954bf 878 for (int K = 3; K != sysconf(_SC_OPEN_MAX); K++)
75ef8f14
MV
879 {
880 if(KeepFDs.find(K) == KeepFDs.end())
007dc9e0 881 fcntl(K,F_SETFD,FD_CLOEXEC);
75ef8f14 882 }
54676e1a
AL
883 }
884
885 return Process;
886}
887 /*}}}*/
ddc1d8d0
AL
888// ExecWait - Fancy waitpid /*{{{*/
889// ---------------------------------------------------------------------
2c9a72d1 890/* Waits for the given sub process. If Reap is set then no errors are
ddc1d8d0
AL
891 generated. Otherwise a failed subprocess will generate a proper descriptive
892 message */
3826564e 893bool ExecWait(pid_t Pid,const char *Name,bool Reap)
ddc1d8d0
AL
894{
895 if (Pid <= 1)
896 return true;
897
898 // Wait and collect the error code
899 int Status;
900 while (waitpid(Pid,&Status,0) != Pid)
901 {
902 if (errno == EINTR)
903 continue;
904
905 if (Reap == true)
906 return false;
907
db0db9fe 908 return _error->Error(_("Waited for %s but it wasn't there"),Name);
ddc1d8d0
AL
909 }
910
911
912 // Check for an error code.
913 if (WIFEXITED(Status) == 0 || WEXITSTATUS(Status) != 0)
914 {
915 if (Reap == true)
916 return false;
ab7f4d7c 917 if (WIFSIGNALED(Status) != 0)
40e7fe0e 918 {
ab7f4d7c
MV
919 if( WTERMSIG(Status) == SIGSEGV)
920 return _error->Error(_("Sub-process %s received a segmentation fault."),Name);
921 else
922 return _error->Error(_("Sub-process %s received signal %u."),Name, WTERMSIG(Status));
40e7fe0e 923 }
ddc1d8d0
AL
924
925 if (WIFEXITED(Status) != 0)
b2e465d6 926 return _error->Error(_("Sub-process %s returned an error code (%u)"),Name,WEXITSTATUS(Status));
ddc1d8d0 927
b2e465d6 928 return _error->Error(_("Sub-process %s exited unexpectedly"),Name);
ddc1d8d0
AL
929 }
930
931 return true;
932}
933 /*}}}*/
578bfd0a 934
13d87e2e 935// FileFd::Open - Open a file /*{{{*/
578bfd0a
AL
936// ---------------------------------------------------------------------
937/* The most commonly used open mode combinations are given with Mode */
52b47296 938bool FileFd::Open(string FileName,unsigned int const Mode,CompressMode Compress, unsigned long const Perms)
578bfd0a 939{
257e8d66
DK
940 if (Mode == ReadOnlyGzip)
941 return Open(FileName, ReadOnly, Gzip, Perms);
257e8d66 942
468720c5 943 if (Compress == Auto && (Mode & WriteOnly) == WriteOnly)
ae635e3c 944 return FileFdError("Autodetection on %s only works in ReadOnly openmode!", FileName.c_str());
257e8d66 945
468720c5
DK
946 std::vector<APT::Configuration::Compressor> const compressors = APT::Configuration::getCompressors();
947 std::vector<APT::Configuration::Compressor>::const_iterator compressor = compressors.begin();
948 if (Compress == Auto)
949 {
468720c5
DK
950 for (; compressor != compressors.end(); ++compressor)
951 {
e788a834 952 std::string file = FileName + compressor->Extension;
468720c5
DK
953 if (FileExists(file) == false)
954 continue;
955 FileName = file;
468720c5
DK
956 break;
957 }
958 }
959 else if (Compress == Extension)
960 {
52b47296
DK
961 std::string::size_type const found = FileName.find_last_of('.');
962 std::string ext;
963 if (found != std::string::npos)
964 {
965 ext = FileName.substr(found);
966 if (ext == ".new" || ext == ".bak")
967 {
968 std::string::size_type const found2 = FileName.find_last_of('.', found - 1);
969 if (found2 != std::string::npos)
970 ext = FileName.substr(found2, found - found2);
971 else
972 ext.clear();
973 }
974 }
aee1aac6
DK
975 for (; compressor != compressors.end(); ++compressor)
976 if (ext == compressor->Extension)
977 break;
978 // no matching extension - assume uncompressed (imagine files like 'example.org_Packages')
979 if (compressor == compressors.end())
980 for (compressor = compressors.begin(); compressor != compressors.end(); ++compressor)
981 if (compressor->Name == ".")
468720c5 982 break;
468720c5 983 }
aee1aac6 984 else
468720c5
DK
985 {
986 std::string name;
987 switch (Compress)
988 {
aee1aac6 989 case None: name = "."; break;
468720c5
DK
990 case Gzip: name = "gzip"; break;
991 case Bzip2: name = "bzip2"; break;
992 case Lzma: name = "lzma"; break;
993 case Xz: name = "xz"; break;
aee1aac6
DK
994 case Auto:
995 case Extension:
52b47296 996 // Unreachable
ae635e3c 997 return FileFdError("Opening File %s in None, Auto or Extension should be already handled?!?", FileName.c_str());
468720c5
DK
998 }
999 for (; compressor != compressors.end(); ++compressor)
1000 if (compressor->Name == name)
1001 break;
aee1aac6 1002 if (compressor == compressors.end())
ae635e3c 1003 return FileFdError("Can't find a configured compressor %s for file %s", name.c_str(), FileName.c_str());
468720c5
DK
1004 }
1005
aee1aac6 1006 if (compressor == compressors.end())
ae635e3c 1007 return FileFdError("Can't find a match for specified compressor mode for file %s", FileName.c_str());
aee1aac6
DK
1008 return Open(FileName, Mode, *compressor, Perms);
1009}
52b47296 1010bool FileFd::Open(string FileName,unsigned int const Mode,APT::Configuration::Compressor const &compressor, unsigned long const Perms)
aee1aac6
DK
1011{
1012 Close();
aee1aac6
DK
1013 Flags = AutoClose;
1014
1015 if ((Mode & WriteOnly) != WriteOnly && (Mode & (Atomic | Create | Empty | Exclusive)) != 0)
ae635e3c 1016 return FileFdError("ReadOnly mode for %s doesn't accept additional flags!", FileName.c_str());
aee1aac6 1017 if ((Mode & ReadWrite) == 0)
ae635e3c 1018 return FileFdError("No openmode provided in FileFd::Open for %s", FileName.c_str());
468720c5 1019
257e8d66
DK
1020 if ((Mode & Atomic) == Atomic)
1021 {
1022 Flags |= Replace;
257e8d66
DK
1023 }
1024 else if ((Mode & (Exclusive | Create)) == (Exclusive | Create))
1025 {
1026 // for atomic, this will be done by rename in Close()
1027 unlink(FileName.c_str());
1028 }
1029 if ((Mode & Empty) == Empty)
578bfd0a 1030 {
257e8d66
DK
1031 struct stat Buf;
1032 if (lstat(FileName.c_str(),&Buf) == 0 && S_ISLNK(Buf.st_mode))
1033 unlink(FileName.c_str());
1034 }
c4fc2fd7 1035
561f860a
DK
1036 int fileflags = 0;
1037 #define if_FLAGGED_SET(FLAG, MODE) if ((Mode & FLAG) == FLAG) fileflags |= MODE
1038 if_FLAGGED_SET(ReadWrite, O_RDWR);
1039 else if_FLAGGED_SET(ReadOnly, O_RDONLY);
1040 else if_FLAGGED_SET(WriteOnly, O_WRONLY);
4a9db827 1041
561f860a
DK
1042 if_FLAGGED_SET(Create, O_CREAT);
1043 if_FLAGGED_SET(Empty, O_TRUNC);
1044 if_FLAGGED_SET(Exclusive, O_EXCL);
561f860a 1045 #undef if_FLAGGED_SET
52b47296 1046
7335eebe
AGM
1047 if ((Mode & Atomic) == Atomic)
1048 {
1049 char *name = strdup((FileName + ".XXXXXX").c_str());
1050
dc545c0b 1051 if((iFd = mkstemp(name)) == -1)
7335eebe
AGM
1052 {
1053 free(name);
98b69f9d 1054 return FileFdErrno("mkstemp", "Could not create temporary file for %s", FileName.c_str());
7335eebe
AGM
1055 }
1056
1057 TemporaryFileName = string(name);
7335eebe 1058 free(name);
dc545c0b
DK
1059
1060 if(Perms != 600 && fchmod(iFd, Perms) == -1)
1061 return FileFdErrno("fchmod", "Could not change permissions for temporary file %s", TemporaryFileName.c_str());
7335eebe 1062 }
468720c5 1063 else
561f860a 1064 iFd = open(FileName.c_str(), fileflags, Perms);
468720c5 1065
b711c01e 1066 this->FileName = FileName;
561f860a
DK
1067 if (iFd == -1 || OpenInternDescriptor(Mode, compressor) == false)
1068 {
468720c5 1069 if (iFd != -1)
fc81e8f2 1070 {
561f860a
DK
1071 close (iFd);
1072 iFd = -1;
fc81e8f2 1073 }
ae635e3c 1074 return FileFdErrno("open",_("Could not open file %s"), FileName.c_str());
257e8d66 1075 }
578bfd0a 1076
13d87e2e
AL
1077 SetCloseExec(iFd,true);
1078 return true;
578bfd0a 1079}
257e8d66
DK
1080 /*}}}*/
1081// FileFd::OpenDescriptor - Open a filedescriptor /*{{{*/
1082// ---------------------------------------------------------------------
1083/* */
52b47296 1084bool FileFd::OpenDescriptor(int Fd, unsigned int const Mode, CompressMode Compress, bool AutoClose)
aee1aac6
DK
1085{
1086 std::vector<APT::Configuration::Compressor> const compressors = APT::Configuration::getCompressors();
1087 std::vector<APT::Configuration::Compressor>::const_iterator compressor = compressors.begin();
1088 std::string name;
bce778a3
MV
1089
1090 // compat with the old API
1091 if (Mode == ReadOnlyGzip && Compress == None)
1092 Compress = Gzip;
1093
aee1aac6
DK
1094 switch (Compress)
1095 {
1096 case None: name = "."; break;
1097 case Gzip: name = "gzip"; break;
1098 case Bzip2: name = "bzip2"; break;
1099 case Lzma: name = "lzma"; break;
1100 case Xz: name = "xz"; break;
1101 case Auto:
1102 case Extension:
f97bb523
DK
1103 if (AutoClose == true && Fd != -1)
1104 close(Fd);
ae635e3c 1105 return FileFdError("Opening Fd %d in Auto or Extension compression mode is not supported", Fd);
aee1aac6
DK
1106 }
1107 for (; compressor != compressors.end(); ++compressor)
1108 if (compressor->Name == name)
1109 break;
1110 if (compressor == compressors.end())
f97bb523
DK
1111 {
1112 if (AutoClose == true && Fd != -1)
1113 close(Fd);
ae635e3c 1114 return FileFdError("Can't find a configured compressor %s for file %s", name.c_str(), FileName.c_str());
f97bb523 1115 }
aee1aac6
DK
1116 return OpenDescriptor(Fd, Mode, *compressor, AutoClose);
1117}
52b47296 1118bool FileFd::OpenDescriptor(int Fd, unsigned int const Mode, APT::Configuration::Compressor const &compressor, bool AutoClose)
144c0969
JAK
1119{
1120 Close();
1121 Flags = (AutoClose) ? FileFd::AutoClose : 0;
84baaae9 1122 iFd = Fd;
b711c01e 1123 this->FileName = "";
84baaae9 1124 if (OpenInternDescriptor(Mode, compressor) == false)
468720c5 1125 {
f97bb523 1126 if (iFd != -1 && (
84baaae9 1127 (Flags & Compressed) == Compressed ||
f97bb523
DK
1128 AutoClose == true))
1129 {
468720c5 1130 close (iFd);
f97bb523
DK
1131 iFd = -1;
1132 }
1133 return FileFdError(_("Could not open file descriptor %d"), Fd);
144c0969 1134 }
144c0969 1135 return true;
468720c5 1136}
52b47296 1137bool FileFd::OpenInternDescriptor(unsigned int const Mode, APT::Configuration::Compressor const &compressor)
468720c5 1138{
84baaae9
DK
1139 if (iFd == -1)
1140 return false;
ff477ee1
DK
1141 if (compressor.Name == "." || compressor.Binary.empty() == true)
1142 return true;
1143
7f350a37 1144#if defined HAVE_ZLIB || defined HAVE_BZ2 || defined HAVE_LZMA
69d6988a
DK
1145 // the API to open files is similar, so setup to avoid code duplicates later
1146 // and while at it ensure that we close before opening (if its a reopen)
1147 void* (*compress_open)(int, const char *) = NULL;
7f350a37
DK
1148 if (false)
1149 /* dummy so that the rest can be 'else if's */;
69d6988a 1150#define APT_COMPRESS_INIT(NAME,OPEN,CLOSE,STRUCT) \
7f350a37 1151 else if (compressor.Name == NAME) \
69d6988a
DK
1152 { \
1153 compress_open = (void*(*)(int, const char *)) OPEN; \
1154 if (d != NULL && STRUCT != NULL) { CLOSE(STRUCT); STRUCT = NULL; } \
1155 }
1156#ifdef HAVE_ZLIB
1157 APT_COMPRESS_INIT("gzip", gzdopen, gzclose, d->gz)
1158#endif
1159#ifdef HAVE_BZ2
1160 APT_COMPRESS_INIT("bzip2", BZ2_bzdopen, BZ2_bzclose, d->bz2)
1161#endif
7f350a37
DK
1162#ifdef HAVE_LZMA
1163 else if (compressor.Name == "xz" || compressor.Name == "lzma")
1164 {
1165 compress_open = (void*(*)(int, const char*)) fdopen;
1166 if (d != NULL && d->lzma != NULL)
1167 {
1168 delete d->lzma;
1169 d->lzma = NULL;
1170 }
1171 }
1172#endif
69d6988a
DK
1173#undef APT_COMPRESS_INIT
1174#endif
1175
ba667cf7
DK
1176 if (d == NULL)
1177 {
1178 d = new FileFdPrivate();
1179 d->openmode = Mode;
1180 d->compressor = compressor;
7f350a37 1181#if defined HAVE_ZLIB || defined HAVE_BZ2 || defined HAVE_LZMA
69d6988a 1182 if (AutoClose == false && compress_open != NULL)
84baaae9
DK
1183 {
1184 // Need to duplicate fd here or gz/bz2 close for cleanup will close the fd as well
1185 int const internFd = dup(iFd);
1186 if (internFd == -1)
1187 return FileFdErrno("OpenInternDescriptor", _("Could not open file descriptor %d"), iFd);
1188 iFd = internFd;
1189 }
69d6988a 1190#endif
ba667cf7 1191 }
ff477ee1 1192
7f350a37 1193#if defined HAVE_ZLIB || defined HAVE_BZ2 || defined HAVE_LZMA
69d6988a 1194 if (compress_open != NULL)
468720c5 1195 {
69d6988a 1196 void* compress_struct = NULL;
468720c5 1197 if ((Mode & ReadWrite) == ReadWrite)
69d6988a 1198 compress_struct = compress_open(iFd, "r+");
468720c5 1199 else if ((Mode & WriteOnly) == WriteOnly)
69d6988a 1200 compress_struct = compress_open(iFd, "w");
468720c5 1201 else
69d6988a
DK
1202 compress_struct = compress_open(iFd, "r");
1203 if (compress_struct == NULL)
468720c5 1204 return false;
69d6988a 1205
7f350a37
DK
1206 if (false)
1207 /* dummy so that the rest can be 'else if's */;
69d6988a 1208#ifdef HAVE_ZLIB
7f350a37 1209 else if (compressor.Name == "gzip")
69d6988a 1210 d->gz = (gzFile) compress_struct;
699b209e 1211#endif
c4997486 1212#ifdef HAVE_BZ2
7f350a37 1213 else if (compressor.Name == "bzip2")
69d6988a 1214 d->bz2 = (BZFILE*) compress_struct;
7f350a37
DK
1215#endif
1216#ifdef HAVE_LZMA
1217 else if (compressor.Name == "xz" || compressor.Name == "lzma")
1218 {
1219 uint32_t const xzlevel = 6;
1220 uint64_t const memlimit = UINT64_MAX;
1221 if (d->lzma == NULL)
1222 d->lzma = new FileFdPrivate::LZMAFILE;
1223 d->lzma->file = (FILE*) compress_struct;
1224 d->lzma->stream = LZMA_STREAM_INIT;
1225
1226 if ((Mode & ReadWrite) == ReadWrite)
1227 return FileFdError("ReadWrite mode is not supported for file %s", FileName.c_str());
1228
1229 if ((Mode & WriteOnly) == WriteOnly)
1230 {
1231 if (compressor.Name == "xz")
1232 {
1233 if (lzma_easy_encoder(&d->lzma->stream, xzlevel, LZMA_CHECK_CRC32) != LZMA_OK)
1234 return false;
1235 }
1236 else
1237 {
1238 lzma_options_lzma options;
1239 lzma_lzma_preset(&options, xzlevel);
1240 if (lzma_alone_encoder(&d->lzma->stream, &options) != LZMA_OK)
1241 return false;
1242 }
1243 d->lzma->compressing = true;
1244 }
1245 else
1246 {
1247 if (compressor.Name == "xz")
1248 {
1249 if (lzma_auto_decoder(&d->lzma->stream, memlimit, 0) != LZMA_OK)
1250 return false;
1251 }
1252 else
1253 {
1254 if (lzma_alone_decoder(&d->lzma->stream, memlimit) != LZMA_OK)
1255 return false;
1256 }
1257 d->lzma->compressing = false;
1258 }
1259 }
69d6988a 1260#endif
c4997486
DK
1261 Flags |= Compressed;
1262 return true;
1263 }
1264#endif
1265
adbd7eb4
DK
1266 // collect zombies here in case we reopen
1267 if (d->compressor_pid > 0)
1268 ExecWait(d->compressor_pid, "FileFdCompressor", true);
561f860a
DK
1269
1270 if ((Mode & ReadWrite) == ReadWrite)
ae635e3c 1271 return FileFdError("ReadWrite mode is not supported for file %s", FileName.c_str());
561f860a
DK
1272
1273 bool const Comp = (Mode & WriteOnly) == WriteOnly;
561f860a
DK
1274 if (Comp == false)
1275 {
adbd7eb4 1276 // Handle 'decompression' of empty files
561f860a
DK
1277 struct stat Buf;
1278 fstat(iFd, &Buf);
1279 if (Buf.st_size == 0 && S_ISFIFO(Buf.st_mode) == false)
1280 return true;
1281
1282 // We don't need the file open - instead let the compressor open it
1283 // as he properly knows better how to efficiently read from 'his' file
1284 if (FileName.empty() == false)
afb093cd 1285 {
561f860a 1286 close(iFd);
afb093cd
DK
1287 iFd = -1;
1288 }
561f860a
DK
1289 }
1290
1291 // Create a data pipe
1292 int Pipe[2] = {-1,-1};
1293 if (pipe(Pipe) != 0)
ae635e3c 1294 return FileFdErrno("pipe",_("Failed to create subprocess IPC"));
561f860a
DK
1295 for (int J = 0; J != 2; J++)
1296 SetCloseExec(Pipe[J],true);
1297
1298 d->compressed_fd = iFd;
1299 d->pipe = true;
1300
1301 if (Comp == true)
1302 iFd = Pipe[1];
1303 else
1304 iFd = Pipe[0];
1305
1306 // The child..
1307 d->compressor_pid = ExecFork();
1308 if (d->compressor_pid == 0)
1309 {
1310 if (Comp == true)
1311 {
1312 dup2(d->compressed_fd,STDOUT_FILENO);
1313 dup2(Pipe[0],STDIN_FILENO);
1314 }
1315 else
1316 {
7f350a37 1317 if (d->compressed_fd != -1)
561f860a
DK
1318 dup2(d->compressed_fd,STDIN_FILENO);
1319 dup2(Pipe[1],STDOUT_FILENO);
1320 }
0b4895d3
DK
1321 int const nullfd = open("/dev/null", O_WRONLY);
1322 if (nullfd != -1)
1323 {
1324 dup2(nullfd,STDERR_FILENO);
1325 close(nullfd);
1326 }
561f860a
DK
1327
1328 SetCloseExec(STDOUT_FILENO,false);
1329 SetCloseExec(STDIN_FILENO,false);
1330
1331 std::vector<char const*> Args;
1332 Args.push_back(compressor.Binary.c_str());
1333 std::vector<std::string> const * const addArgs =
1334 (Comp == true) ? &(compressor.CompressArgs) : &(compressor.UncompressArgs);
1335 for (std::vector<std::string>::const_iterator a = addArgs->begin();
1336 a != addArgs->end(); ++a)
1337 Args.push_back(a->c_str());
1338 if (Comp == false && FileName.empty() == false)
1339 {
1340 Args.push_back("--stdout");
1341 if (TemporaryFileName.empty() == false)
1342 Args.push_back(TemporaryFileName.c_str());
1343 else
1344 Args.push_back(FileName.c_str());
1345 }
1346 Args.push_back(NULL);
1347
1348 execvp(Args[0],(char **)&Args[0]);
1349 cerr << _("Failed to exec compressor ") << Args[0] << endl;
1350 _exit(100);
1351 }
1352 if (Comp == true)
1353 close(Pipe[0]);
468720c5 1354 else
561f860a 1355 close(Pipe[1]);
561f860a 1356
468720c5 1357 return true;
144c0969 1358}
578bfd0a 1359 /*}}}*/
8e06abb2 1360// FileFd::~File - Closes the file /*{{{*/
578bfd0a
AL
1361// ---------------------------------------------------------------------
1362/* If the proper modes are selected then we close the Fd and possibly
1363 unlink the file on error. */
8e06abb2 1364FileFd::~FileFd()
578bfd0a
AL
1365{
1366 Close();
500400fe 1367 if (d != NULL)
500400fe 1368 d->CloseDown(FileName);
96ab3c6f
MV
1369 delete d;
1370 d = NULL;
578bfd0a
AL
1371}
1372 /*}}}*/
8e06abb2 1373// FileFd::Read - Read a bit of the file /*{{{*/
578bfd0a 1374// ---------------------------------------------------------------------
1e3f4083 1375/* We are careful to handle interruption by a signal while reading
b0db36b1 1376 gracefully. */
650faab0 1377bool FileFd::Read(void *To,unsigned long long Size,unsigned long long *Actual)
578bfd0a 1378{
3286ad13 1379 ssize_t Res;
b0db36b1 1380 errno = 0;
f604cf55
AL
1381 if (Actual != 0)
1382 *Actual = 0;
699b209e 1383 *((char *)To) = '\0';
b0db36b1 1384 do
578bfd0a 1385 {
7f350a37
DK
1386 if (false)
1387 /* dummy so that the rest can be 'else if's */;
7efb8c8e 1388#ifdef HAVE_ZLIB
7f350a37 1389 else if (d != NULL && d->gz != NULL)
c4997486 1390 Res = gzread(d->gz,To,Size);
c4997486
DK
1391#endif
1392#ifdef HAVE_BZ2
7f350a37 1393 else if (d != NULL && d->bz2 != NULL)
c4997486 1394 Res = BZ2_bzread(d->bz2,To,Size);
699b209e 1395#endif
7f350a37
DK
1396#ifdef HAVE_LZMA
1397 else if (d != NULL && d->lzma != NULL)
1398 {
1399 if (d->lzma->eof == true)
1400 break;
1401
1402 d->lzma->stream.next_out = (uint8_t *) To;
1403 d->lzma->stream.avail_out = Size;
1404 if (d->lzma->stream.avail_in == 0)
1405 {
1406 d->lzma->stream.next_in = d->lzma->buffer;
1407 d->lzma->stream.avail_in = fread(d->lzma->buffer, 1, sizeof(d->lzma->buffer)/sizeof(d->lzma->buffer[0]), d->lzma->file);
1408 }
1409 d->lzma->err = lzma_code(&d->lzma->stream, LZMA_RUN);
1410 if (d->lzma->err == LZMA_STREAM_END)
1411 {
1412 d->lzma->eof = true;
1413 Res = Size - d->lzma->stream.avail_out;
1414 }
1415 else if (d->lzma->err != LZMA_OK)
1416 {
1417 Res = -1;
1418 errno = 0;
1419 }
1420 else
1421 Res = Size - d->lzma->stream.avail_out;
1422 }
1423#endif
1424 else
a3a03f5d 1425 Res = read(iFd,To,Size);
b711c01e 1426
b0db36b1
AL
1427 if (Res < 0)
1428 {
b711c01e
DK
1429 if (errno == EINTR)
1430 continue;
7f350a37
DK
1431 if (false)
1432 /* dummy so that the rest can be 'else if's */;
7efb8c8e 1433#ifdef HAVE_ZLIB
7f350a37 1434 else if (d != NULL && d->gz != NULL)
b711c01e
DK
1435 {
1436 int err;
1437 char const * const errmsg = gzerror(d->gz, &err);
1438 if (err != Z_ERRNO)
ae635e3c 1439 return FileFdError("gzread: %s (%d: %s)", _("Read error"), err, errmsg);
b711c01e 1440 }
c4997486
DK
1441#endif
1442#ifdef HAVE_BZ2
7f350a37 1443 else if (d != NULL && d->bz2 != NULL)
c4997486
DK
1444 {
1445 int err;
1446 char const * const errmsg = BZ2_bzerror(d->bz2, &err);
1447 if (err != BZ_IO_ERROR)
ae635e3c 1448 return FileFdError("BZ2_bzread: %s (%d: %s)", _("Read error"), err, errmsg);
c4997486 1449 }
7f350a37
DK
1450#endif
1451#ifdef HAVE_LZMA
1452 else if (d != NULL && d->lzma != NULL)
1453 return FileFdError("lzma_read: %s (%d)", _("Read error"), d->lzma->err);
b711c01e 1454#endif
ae635e3c 1455 return FileFdErrno("read",_("Read error"));
b0db36b1 1456 }
578bfd0a 1457
b0db36b1
AL
1458 To = (char *)To + Res;
1459 Size -= Res;
ff477ee1
DK
1460 if (d != NULL)
1461 d->seekpos += Res;
f604cf55
AL
1462 if (Actual != 0)
1463 *Actual += Res;
b0db36b1
AL
1464 }
1465 while (Res > 0 && Size > 0);
1466
1467 if (Size == 0)
1468 return true;
1469
ddc1d8d0 1470 // Eof handling
f604cf55 1471 if (Actual != 0)
ddc1d8d0
AL
1472 {
1473 Flags |= HitEof;
1474 return true;
1475 }
ae635e3c
DK
1476
1477 return FileFdError(_("read, still have %llu to read but none left"), Size);
578bfd0a
AL
1478}
1479 /*}}}*/
032bd56f
DK
1480// FileFd::ReadLine - Read a complete line from the file /*{{{*/
1481// ---------------------------------------------------------------------
1482/* Beware: This method can be quiet slow for big buffers on UNcompressed
1483 files because of the naive implementation! */
1484char* FileFd::ReadLine(char *To, unsigned long long const Size)
1485{
699b209e 1486 *To = '\0';
7efb8c8e 1487#ifdef HAVE_ZLIB
ff477ee1 1488 if (d != NULL && d->gz != NULL)
032bd56f 1489 return gzgets(d->gz, To, Size);
699b209e 1490#endif
032bd56f
DK
1491
1492 unsigned long long read = 0;
40468850
DK
1493 while ((Size - 1) != read)
1494 {
1495 unsigned long long done = 0;
1496 if (Read(To + read, 1, &done) == false)
1497 return NULL;
1498 if (done == 0)
1499 break;
1500 if (To[read++] == '\n')
1501 break;
1502 }
1503 if (read == 0)
032bd56f 1504 return NULL;
40468850 1505 To[read] = '\0';
032bd56f
DK
1506 return To;
1507}
1508 /*}}}*/
8e06abb2 1509// FileFd::Write - Write to the file /*{{{*/
578bfd0a
AL
1510// ---------------------------------------------------------------------
1511/* */
650faab0 1512bool FileFd::Write(const void *From,unsigned long long Size)
578bfd0a 1513{
3286ad13 1514 ssize_t Res;
b0db36b1
AL
1515 errno = 0;
1516 do
578bfd0a 1517 {
7f350a37
DK
1518 if (false)
1519 /* dummy so that the rest can be 'else if's */;
7efb8c8e 1520#ifdef HAVE_ZLIB
7f350a37
DK
1521 else if (d != NULL && d->gz != NULL)
1522 Res = gzwrite(d->gz,From,Size);
c4997486
DK
1523#endif
1524#ifdef HAVE_BZ2
7f350a37
DK
1525 else if (d != NULL && d->bz2 != NULL)
1526 Res = BZ2_bzwrite(d->bz2,(void*)From,Size);
699b209e 1527#endif
7f350a37
DK
1528#ifdef HAVE_LZMA
1529 else if (d != NULL && d->lzma != NULL)
1530 {
1531 d->lzma->stream.next_in = (uint8_t *)From;
1532 d->lzma->stream.avail_in = Size;
1533 d->lzma->stream.next_out = d->lzma->buffer;
1534 d->lzma->stream.avail_out = sizeof(d->lzma->buffer)/sizeof(d->lzma->buffer[0]);
1535 d->lzma->err = lzma_code(&d->lzma->stream, LZMA_RUN);
1536 if (d->lzma->err != LZMA_OK)
1537 return false;
1538 size_t const n = sizeof(d->lzma->buffer)/sizeof(d->lzma->buffer[0]) - d->lzma->stream.avail_out;
1539 size_t const m = (n == 0) ? 0 : fwrite(d->lzma->buffer, 1, n, d->lzma->file);
1540 if (m != n)
1541 Res = -1;
1542 else
1543 Res = Size - d->lzma->stream.avail_in;
1544 }
1545#endif
1546 else
1547 Res = write(iFd,From,Size);
1548
b0db36b1
AL
1549 if (Res < 0 && errno == EINTR)
1550 continue;
1551 if (Res < 0)
1552 {
7f350a37
DK
1553 if (false)
1554 /* dummy so that the rest can be 'else if's */;
c4997486 1555#ifdef HAVE_ZLIB
7f350a37 1556 else if (d != NULL && d->gz != NULL)
c4997486
DK
1557 {
1558 int err;
1559 char const * const errmsg = gzerror(d->gz, &err);
1560 if (err != Z_ERRNO)
ae635e3c 1561 return FileFdError("gzwrite: %s (%d: %s)", _("Write error"), err, errmsg);
c4997486
DK
1562 }
1563#endif
1564#ifdef HAVE_BZ2
7f350a37 1565 else if (d != NULL && d->bz2 != NULL)
c4997486
DK
1566 {
1567 int err;
1568 char const * const errmsg = BZ2_bzerror(d->bz2, &err);
1569 if (err != BZ_IO_ERROR)
ae635e3c 1570 return FileFdError("BZ2_bzwrite: %s (%d: %s)", _("Write error"), err, errmsg);
c4997486 1571 }
7f350a37
DK
1572#endif
1573#ifdef HAVE_LZMA
1574 else if (d != NULL && d->lzma != NULL)
1575 return FileFdErrno("lzma_fwrite", _("Write error"));
c4997486 1576#endif
ae635e3c 1577 return FileFdErrno("write",_("Write error"));
b0db36b1
AL
1578 }
1579
cf4ff3b7 1580 From = (char const *)From + Res;
b0db36b1 1581 Size -= Res;
ff477ee1
DK
1582 if (d != NULL)
1583 d->seekpos += Res;
578bfd0a 1584 }
b0db36b1 1585 while (Res > 0 && Size > 0);
578bfd0a 1586
b0db36b1
AL
1587 if (Size == 0)
1588 return true;
ae635e3c
DK
1589
1590 return FileFdError(_("write, still have %llu to write but couldn't"), Size);
d68d65ad
DK
1591}
1592bool FileFd::Write(int Fd, const void *From, unsigned long long Size)
1593{
3286ad13 1594 ssize_t Res;
d68d65ad
DK
1595 errno = 0;
1596 do
1597 {
1598 Res = write(Fd,From,Size);
1599 if (Res < 0 && errno == EINTR)
1600 continue;
1601 if (Res < 0)
1602 return _error->Errno("write",_("Write error"));
1603
cf4ff3b7 1604 From = (char const *)From + Res;
d68d65ad
DK
1605 Size -= Res;
1606 }
1607 while (Res > 0 && Size > 0);
1608
1609 if (Size == 0)
1610 return true;
1611
1612 return _error->Error(_("write, still have %llu to write but couldn't"), Size);
578bfd0a
AL
1613}
1614 /*}}}*/
8e06abb2 1615// FileFd::Seek - Seek in the file /*{{{*/
578bfd0a
AL
1616// ---------------------------------------------------------------------
1617/* */
650faab0 1618bool FileFd::Seek(unsigned long long To)
578bfd0a 1619{
ff477ee1 1620 if (d != NULL && (d->pipe == true
c4997486 1621#ifdef HAVE_BZ2
ff477ee1 1622 || d->bz2 != NULL
7f350a37
DK
1623#endif
1624#ifdef HAVE_LZMA
1625 || d->lzma != NULL
c4997486 1626#endif
ff477ee1 1627 ))
699b209e 1628 {
1abbc47c
DK
1629 // Our poor man seeking in pipes is costly, so try to avoid it
1630 unsigned long long seekpos = Tell();
1631 if (seekpos == To)
1632 return true;
1633 else if (seekpos < To)
1634 return Skip(To - seekpos);
1635
561f860a 1636 if ((d->openmode & ReadOnly) != ReadOnly)
ae635e3c 1637 return FileFdError("Reopen is only implemented for read-only files!");
7f350a37
DK
1638 if (false)
1639 /* dummy so that the rest can be 'else if's */;
c4997486 1640#ifdef HAVE_BZ2
7f350a37
DK
1641 else if (d->bz2 != NULL)
1642 {
1643 BZ2_bzclose(d->bz2);
1644 d->bz2 = NULL;
1645 }
1646#endif
1647#ifdef HAVE_LZMA
1648 else if (d->lzma != NULL)
1649 {
1650 delete d->lzma;
1651 d->lzma = NULL;
1652 }
c4997486 1653#endif
afb093cd
DK
1654 if (iFd != -1)
1655 close(iFd);
1656 iFd = -1;
561f860a
DK
1657 if (TemporaryFileName.empty() == false)
1658 iFd = open(TemporaryFileName.c_str(), O_RDONLY);
1659 else if (FileName.empty() == false)
1660 iFd = open(FileName.c_str(), O_RDONLY);
1661 else
6fd947bd
DK
1662 {
1663 if (d->compressed_fd > 0)
1664 if (lseek(d->compressed_fd, 0, SEEK_SET) != 0)
1665 iFd = d->compressed_fd;
500400fe 1666 if (iFd < 0)
ae635e3c 1667 return FileFdError("Reopen is not implemented for pipes opened with FileFd::OpenDescriptor()!");
6fd947bd 1668 }
561f860a
DK
1669
1670 if (OpenInternDescriptor(d->openmode, d->compressor) == false)
ae635e3c 1671 return FileFdError("Seek on file %s because it couldn't be reopened", FileName.c_str());
561f860a
DK
1672
1673 if (To != 0)
1674 return Skip(To);
1abbc47c
DK
1675
1676 d->seekpos = To;
561f860a 1677 return true;
699b209e 1678 }
3286ad13 1679 off_t res;
7efb8c8e 1680#ifdef HAVE_ZLIB
ff477ee1 1681 if (d != NULL && d->gz)
032bd56f 1682 res = gzseek(d->gz,To,SEEK_SET);
a3a03f5d 1683 else
699b209e 1684#endif
a3a03f5d 1685 res = lseek(iFd,To,SEEK_SET);
3286ad13 1686 if (res != (off_t)To)
ae635e3c 1687 return FileFdError("Unable to seek to %llu", To);
1abbc47c 1688
ff477ee1
DK
1689 if (d != NULL)
1690 d->seekpos = To;
727f18af
AL
1691 return true;
1692}
1693 /*}}}*/
1694// FileFd::Skip - Seek in the file /*{{{*/
1695// ---------------------------------------------------------------------
1696/* */
650faab0 1697bool FileFd::Skip(unsigned long long Over)
727f18af 1698{
ff477ee1 1699 if (d != NULL && (d->pipe == true
c4997486 1700#ifdef HAVE_BZ2
ff477ee1 1701 || d->bz2 != NULL
7f350a37
DK
1702#endif
1703#ifdef HAVE_LZMA
1704 || d->lzma != NULL
c4997486 1705#endif
ff477ee1 1706 ))
40468850
DK
1707 {
1708 d->seekpos += Over;
1709 char buffer[1024];
1710 while (Over != 0)
1711 {
1712 unsigned long long toread = std::min((unsigned long long) sizeof(buffer), Over);
1713 if (Read(buffer, toread) == false)
ae635e3c 1714 return FileFdError("Unable to seek ahead %llu",Over);
40468850
DK
1715 Over -= toread;
1716 }
1717 return true;
1718 }
1719
3286ad13 1720 off_t res;
7efb8c8e 1721#ifdef HAVE_ZLIB
ff477ee1 1722 if (d != NULL && d->gz != NULL)
032bd56f 1723 res = gzseek(d->gz,Over,SEEK_CUR);
a3a03f5d 1724 else
699b209e 1725#endif
a3a03f5d 1726 res = lseek(iFd,Over,SEEK_CUR);
1727 if (res < 0)
ae635e3c 1728 return FileFdError("Unable to seek ahead %llu",Over);
ff477ee1
DK
1729 if (d != NULL)
1730 d->seekpos = res;
1abbc47c 1731
6d5dd02a
AL
1732 return true;
1733}
1734 /*}}}*/
1735// FileFd::Truncate - Truncate the file /*{{{*/
1736// ---------------------------------------------------------------------
1737/* */
650faab0 1738bool FileFd::Truncate(unsigned long long To)
6d5dd02a 1739{
ad5051ef
DK
1740 // truncating /dev/null is always successful - as we get an error otherwise
1741 if (To == 0 && FileName == "/dev/null")
1742 return true;
7f350a37
DK
1743#if defined HAVE_ZLIB || defined HAVE_BZ2 || defined HAVE_LZMA
1744 if (d != NULL && (
1745#ifdef HAVE_ZLIB
1746 d->gz != NULL ||
1747#endif
1748#ifdef HAVE_BZ2
1749 d->bz2 != NULL ||
1750#endif
1751#ifdef HAVE_LZMA
1752 d->lzma != NULL ||
1753#endif
1754 false))
ae635e3c 1755 return FileFdError("Truncating compressed files is not implemented (%s)", FileName.c_str());
c4997486 1756#endif
6d5dd02a 1757 if (ftruncate(iFd,To) != 0)
ae635e3c
DK
1758 return FileFdError("Unable to truncate to %llu",To);
1759
578bfd0a
AL
1760 return true;
1761}
1762 /*}}}*/
7f25bdff
AL
1763// FileFd::Tell - Current seek position /*{{{*/
1764// ---------------------------------------------------------------------
1765/* */
650faab0 1766unsigned long long FileFd::Tell()
7f25bdff 1767{
1abbc47c
DK
1768 // In theory, we could just return seekpos here always instead of
1769 // seeking around, but not all users of FileFd use always Seek() and co
1770 // so d->seekpos isn't always true and we can just use it as a hint if
1771 // we have nothing else, but not always as an authority…
ff477ee1 1772 if (d != NULL && (d->pipe == true
c4997486 1773#ifdef HAVE_BZ2
ff477ee1 1774 || d->bz2 != NULL
7f350a37
DK
1775#endif
1776#ifdef HAVE_LZMA
1777 || d->lzma != NULL
c4997486 1778#endif
ff477ee1 1779 ))
1abbc47c
DK
1780 return d->seekpos;
1781
a3a03f5d 1782 off_t Res;
7efb8c8e 1783#ifdef HAVE_ZLIB
ff477ee1 1784 if (d != NULL && d->gz != NULL)
032bd56f 1785 Res = gztell(d->gz);
a3a03f5d 1786 else
699b209e 1787#endif
a3a03f5d 1788 Res = lseek(iFd,0,SEEK_CUR);
7f25bdff 1789 if (Res == (off_t)-1)
ae635e3c 1790 FileFdErrno("lseek","Failed to determine the current file position");
ff477ee1
DK
1791 if (d != NULL)
1792 d->seekpos = Res;
7f25bdff
AL
1793 return Res;
1794}
1795 /*}}}*/
8190b07a 1796static bool StatFileFd(char const * const msg, int const iFd, std::string const &FileName, struct stat &Buf, FileFdPrivate * const d) /*{{{*/
578bfd0a 1797{
6008b79a
DK
1798 bool ispipe = (d != NULL && d->pipe == true);
1799 if (ispipe == false)
1800 {
1801 if (fstat(iFd,&Buf) != 0)
8190b07a
DK
1802 // higher-level code will generate more meaningful messages,
1803 // even translated this would be meaningless for users
1804 return _error->Errno("fstat", "Unable to determine %s for fd %i", msg, iFd);
6008b79a
DK
1805 ispipe = S_ISFIFO(Buf.st_mode);
1806 }
699b209e
DK
1807
1808 // for compressor pipes st_size is undefined and at 'best' zero
6008b79a 1809 if (ispipe == true)
699b209e
DK
1810 {
1811 // we set it here, too, as we get the info here for free
1812 // in theory the Open-methods should take care of it already
ff477ee1
DK
1813 if (d != NULL)
1814 d->pipe = true;
699b209e 1815 if (stat(FileName.c_str(), &Buf) != 0)
8190b07a
DK
1816 return _error->Errno("fstat", "Unable to determine %s for file %s", msg, FileName.c_str());
1817 }
1818 return true;
1819}
1820 /*}}}*/
1821// FileFd::FileSize - Return the size of the file /*{{{*/
1822unsigned long long FileFd::FileSize()
1823{
1824 struct stat Buf;
1825 if (StatFileFd("file size", iFd, FileName, Buf, d) == false)
1826 {
1827 Flags |= Fail;
1828 return 0;
699b209e 1829 }
4260fd39
DK
1830 return Buf.st_size;
1831}
1832 /*}}}*/
8190b07a
DK
1833// FileFd::ModificationTime - Return the time of last touch /*{{{*/
1834time_t FileFd::ModificationTime()
1835{
1836 struct stat Buf;
1837 if (StatFileFd("modification time", iFd, FileName, Buf, d) == false)
1838 {
1839 Flags |= Fail;
1840 return 0;
1841 }
1842 return Buf.st_mtime;
1843}
1844 /*}}}*/
4260fd39
DK
1845// FileFd::Size - Return the size of the content in the file /*{{{*/
1846// ---------------------------------------------------------------------
1847/* */
650faab0 1848unsigned long long FileFd::Size()
4260fd39 1849{
650faab0 1850 unsigned long long size = FileSize();
44dc669e 1851
699b209e
DK
1852 // for compressor pipes st_size is undefined and at 'best' zero,
1853 // so we 'read' the content and 'seek' back - see there
ff477ee1 1854 if (d != NULL && (d->pipe == true
c4997486 1855#ifdef HAVE_BZ2
ff477ee1 1856 || (d->bz2 && size > 0)
7f350a37
DK
1857#endif
1858#ifdef HAVE_LZMA
1859 || (d->lzma && size > 0)
c4997486 1860#endif
ff477ee1 1861 ))
699b209e 1862 {
1abbc47c 1863 unsigned long long const oldSeek = Tell();
699b209e
DK
1864 char ignore[1000];
1865 unsigned long long read = 0;
1866 do {
638593bd
DK
1867 if (Read(ignore, sizeof(ignore), &read) == false)
1868 {
1869 Seek(oldSeek);
1870 return 0;
1871 }
699b209e 1872 } while(read != 0);
1abbc47c
DK
1873 size = Tell();
1874 Seek(oldSeek);
699b209e 1875 }
7efb8c8e 1876#ifdef HAVE_ZLIB
44dc669e 1877 // only check gzsize if we are actually a gzip file, just checking for
032bd56f 1878 // "gz" is not sufficient as uncompressed files could be opened with
44dc669e 1879 // gzopen in "direct" mode as well
ff477ee1 1880 else if (d != NULL && d->gz && !gzdirect(d->gz) && size > 0)
9c182afa 1881 {
65c72a4b 1882 off_t const oldPos = lseek(iFd,0,SEEK_CUR);
9c182afa
MP
1883 /* unfortunately zlib.h doesn't provide a gzsize(), so we have to do
1884 * this ourselves; the original (uncompressed) file size is the last 32
1885 * bits of the file */
650faab0 1886 // FIXME: Size for gz-files is limited by 32bit… no largefile support
9c182afa 1887 if (lseek(iFd, -4, SEEK_END) < 0)
638593bd
DK
1888 {
1889 FileFdErrno("lseek","Unable to seek to end of gzipped file");
1890 return 0;
1891 }
1892 size = 0;
9c182afa 1893 if (read(iFd, &size, 4) != 4)
638593bd
DK
1894 {
1895 FileFdErrno("read","Unable to read original size of gzipped file");
1896 return 0;
1897 }
f330c0f3
DK
1898
1899#ifdef WORDS_BIGENDIAN
2cae0ccb
DK
1900 uint32_t tmp_size = size;
1901 uint8_t const * const p = (uint8_t const * const) &tmp_size;
1902 tmp_size = (p[3] << 24) | (p[2] << 16) | (p[1] << 8) | p[0];
1903 size = tmp_size;
f330c0f3 1904#endif
9c182afa 1905
65c72a4b 1906 if (lseek(iFd, oldPos, SEEK_SET) < 0)
638593bd
DK
1907 {
1908 FileFdErrno("lseek","Unable to seek in gzipped file");
1909 return 0;
1910 }
65c72a4b 1911
9c182afa
MP
1912 return size;
1913 }
699b209e 1914#endif
9c182afa 1915
44dc669e 1916 return size;
578bfd0a
AL
1917}
1918 /*}}}*/
8e06abb2 1919// FileFd::Close - Close the file if the close flag is set /*{{{*/
578bfd0a
AL
1920// ---------------------------------------------------------------------
1921/* */
8e06abb2 1922bool FileFd::Close()
578bfd0a 1923{
032bd56f
DK
1924 if (iFd == -1)
1925 return true;
1926
578bfd0a
AL
1927 bool Res = true;
1928 if ((Flags & AutoClose) == AutoClose)
d13c2d3f 1929 {
500400fe
DK
1930 if ((Flags & Compressed) != Compressed && iFd > 0 && close(iFd) != 0)
1931 Res &= _error->Errno("close",_("Problem closing the file %s"), FileName.c_str());
1932
1933 if (d != NULL)
1934 {
1935 Res &= d->CloseDown(FileName);
1936 delete d;
1937 d = NULL;
1938 }
d13c2d3f 1939 }
3010fb0e 1940
d3aac32e 1941 if ((Flags & Replace) == Replace) {
3010fb0e 1942 if (rename(TemporaryFileName.c_str(), FileName.c_str()) != 0)
62d073d9
DK
1943 Res &= _error->Errno("rename",_("Problem renaming the file %s to %s"), TemporaryFileName.c_str(), FileName.c_str());
1944
fd3b761e 1945 FileName = TemporaryFileName; // for the unlink() below.
257e8d66 1946 TemporaryFileName.clear();
3010fb0e 1947 }
62d073d9
DK
1948
1949 iFd = -1;
1950
578bfd0a
AL
1951 if ((Flags & Fail) == Fail && (Flags & DelOnFail) == DelOnFail &&
1952 FileName.empty() == false)
1953 if (unlink(FileName.c_str()) != 0)
62d073d9 1954 Res &= _error->WarningE("unlnk",_("Problem unlinking the file %s"), FileName.c_str());
3010fb0e 1955
fbb89d94
DK
1956 if (Res == false)
1957 Flags |= Fail;
578bfd0a
AL
1958 return Res;
1959}
1960 /*}}}*/
b2e465d6
AL
1961// FileFd::Sync - Sync the file /*{{{*/
1962// ---------------------------------------------------------------------
1963/* */
1964bool FileFd::Sync()
1965{
b2e465d6 1966 if (fsync(iFd) != 0)
ae635e3c
DK
1967 return FileFdErrno("sync",_("Problem syncing the file"));
1968 return true;
1969}
1970 /*}}}*/
1971// FileFd::FileFdErrno - set Fail and call _error->Errno *{{{*/
1972bool FileFd::FileFdErrno(const char *Function, const char *Description,...)
1973{
1974 Flags |= Fail;
1975 va_list args;
1976 size_t msgSize = 400;
1977 int const errsv = errno;
1978 while (true)
fbb89d94 1979 {
ae635e3c
DK
1980 va_start(args,Description);
1981 if (_error->InsertErrno(GlobalError::ERROR, Function, Description, args, errsv, msgSize) == false)
1982 break;
1983 va_end(args);
fbb89d94 1984 }
ae635e3c
DK
1985 return false;
1986}
1987 /*}}}*/
1988// FileFd::FileFdError - set Fail and call _error->Error *{{{*/
1989bool FileFd::FileFdError(const char *Description,...) {
1990 Flags |= Fail;
1991 va_list args;
1992 size_t msgSize = 400;
1993 while (true)
1994 {
1995 va_start(args,Description);
1996 if (_error->Insert(GlobalError::ERROR, Description, args, msgSize) == false)
1997 break;
1998 va_end(args);
1999 }
2000 return false;
b2e465d6
AL
2001}
2002 /*}}}*/
699b209e 2003
7f350a37
DK
2004APT_DEPRECATED gzFile FileFd::gzFd() {
2005#ifdef HAVE_ZLIB
2006 return d->gz;
2007#else
2008 return NULL;
2009#endif
2010}
488011fa
MV
2011
2012
2013// Glob - wrapper around "glob()" /*{{{*/
2014// ---------------------------------------------------------------------
2015/* */
2016std::vector<std::string> Glob(std::string const &pattern, int flags)
2017{
2018 std::vector<std::string> result;
2019 glob_t globbuf;
ec4835a1
ÁGM
2020 int glob_res;
2021 unsigned int i;
488011fa
MV
2022
2023 glob_res = glob(pattern.c_str(), flags, NULL, &globbuf);
2024
2025 if (glob_res != 0)
2026 {
2027 if(glob_res != GLOB_NOMATCH) {
2028 _error->Errno("glob", "Problem with glob");
2029 return result;
2030 }
2031 }
2032
2033 // append results
2034 for(i=0;i<globbuf.gl_pathc;i++)
2035 result.push_back(string(globbuf.gl_pathv[i]));
2036
2037 globfree(&globbuf);
2038 return result;
2039}
2040 /*}}}*/
68e01721
MV
2041
2042std::string GetTempDir()
2043{
2044 const char *tmpdir = getenv("TMPDIR");
2045
2046#ifdef P_tmpdir
2047 if (!tmpdir)
2048 tmpdir = P_tmpdir;
2049#endif
2050
2051 // check that tmpdir is set and exists
2052 struct stat st;
a077861a 2053 if (!tmpdir || strlen(tmpdir) == 0 || stat(tmpdir, &st) != 0)
68e01721
MV
2054 tmpdir = "/tmp";
2055
2056 return string(tmpdir);
2057}