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