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