]> git.saurik.com Git - apt.git/blame - apt-pkg/contrib/fileutl.cc
apply various style suggestions by cppcheck
[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
15901516 803 long ScOpenMax = sysconf(_SC_OPEN_MAX);
54676e1a 804 // Close all of our FDs - just in case
15901516 805 for (int K = 3; K != ScOpenMax; K++)
75ef8f14
MV
806 {
807 if(KeepFDs.find(K) == KeepFDs.end())
007dc9e0 808 fcntl(K,F_SETFD,FD_CLOEXEC);
75ef8f14 809 }
54676e1a
AL
810 }
811
812 return Process;
813}
814 /*}}}*/
ddc1d8d0
AL
815// ExecWait - Fancy waitpid /*{{{*/
816// ---------------------------------------------------------------------
2c9a72d1 817/* Waits for the given sub process. If Reap is set then no errors are
ddc1d8d0
AL
818 generated. Otherwise a failed subprocess will generate a proper descriptive
819 message */
3826564e 820bool ExecWait(pid_t Pid,const char *Name,bool Reap)
ddc1d8d0
AL
821{
822 if (Pid <= 1)
823 return true;
824
825 // Wait and collect the error code
826 int Status;
827 while (waitpid(Pid,&Status,0) != Pid)
828 {
829 if (errno == EINTR)
830 continue;
831
832 if (Reap == true)
833 return false;
834
db0db9fe 835 return _error->Error(_("Waited for %s but it wasn't there"),Name);
ddc1d8d0
AL
836 }
837
838
839 // Check for an error code.
840 if (WIFEXITED(Status) == 0 || WEXITSTATUS(Status) != 0)
841 {
842 if (Reap == true)
843 return false;
ab7f4d7c 844 if (WIFSIGNALED(Status) != 0)
40e7fe0e 845 {
ab7f4d7c
MV
846 if( WTERMSIG(Status) == SIGSEGV)
847 return _error->Error(_("Sub-process %s received a segmentation fault."),Name);
848 else
849 return _error->Error(_("Sub-process %s received signal %u."),Name, WTERMSIG(Status));
40e7fe0e 850 }
ddc1d8d0
AL
851
852 if (WIFEXITED(Status) != 0)
b2e465d6 853 return _error->Error(_("Sub-process %s returned an error code (%u)"),Name,WEXITSTATUS(Status));
ddc1d8d0 854
b2e465d6 855 return _error->Error(_("Sub-process %s exited unexpectedly"),Name);
ddc1d8d0
AL
856 }
857
858 return true;
859}
860 /*}}}*/
f8aba23f 861// StartsWithGPGClearTextSignature - Check if a file is Pgp/GPG clearsigned /*{{{*/
fe5804fc 862bool StartsWithGPGClearTextSignature(string const &FileName)
0854ad8b
MV
863{
864 static const char* SIGMSG = "-----BEGIN PGP SIGNED MESSAGE-----\n";
1c89c98a 865 char buffer[strlen(SIGMSG)+1];
0854ad8b
MV
866 FILE* gpg = fopen(FileName.c_str(), "r");
867 if (gpg == NULL)
868 return false;
869
870 char const * const test = fgets(buffer, sizeof(buffer), gpg);
871 fclose(gpg);
872 if (test == NULL || strcmp(buffer, SIGMSG) != 0)
873 return false;
874
875 return true;
876}
f8aba23f 877 /*}}}*/
d84da499
DK
878// ChangeOwnerAndPermissionOfFile - set file attributes to requested values /*{{{*/
879bool ChangeOwnerAndPermissionOfFile(char const * const requester, char const * const file, char const * const user, char const * const group, mode_t const mode)
880{
881 if (strcmp(file, "/dev/null") == 0)
882 return true;
883 bool Res = true;
884 if (getuid() == 0 && strlen(user) != 0 && strlen(group) != 0) // if we aren't root, we can't chown, so don't try it
885 {
886 // ensure the file is owned by root and has good permissions
887 struct passwd const * const pw = getpwnam(user);
888 struct group const * const gr = getgrnam(group);
889 if (pw != NULL && gr != NULL && chown(file, pw->pw_uid, gr->gr_gid) != 0)
890 Res &= _error->WarningE(requester, "chown to %s:%s of file %s failed", user, group, file);
891 }
892 if (chmod(file, mode) != 0)
893 Res &= _error->WarningE(requester, "chmod 0%o of file %s failed", mode, file);
894 return Res;
895}
896 /*}}}*/
0854ad8b 897
4239dbca
DK
898class FileFdPrivate { /*{{{*/
899 public:
900#ifdef HAVE_ZLIB
901 gzFile gz;
902#endif
903#ifdef HAVE_BZ2
904 BZFILE* bz2;
905#endif
906#ifdef HAVE_LZMA
907 struct LZMAFILE {
908 FILE* file;
909 uint8_t buffer[4096];
910 lzma_stream stream;
911 lzma_ret err;
912 bool eof;
913 bool compressing;
914
25613a61 915 LZMAFILE() : file(NULL), eof(false), compressing(false) { buffer[0] = '\0'; }
4239dbca
DK
916 ~LZMAFILE() {
917 if (compressing == true)
918 {
919 for (;;) {
920 stream.avail_out = sizeof(buffer)/sizeof(buffer[0]);
921 stream.next_out = buffer;
922 err = lzma_code(&stream, LZMA_FINISH);
923 if (err != LZMA_OK && err != LZMA_STREAM_END)
924 {
925 _error->Error("~LZMAFILE: Compress finalisation failed");
926 break;
927 }
928 size_t const n = sizeof(buffer)/sizeof(buffer[0]) - stream.avail_out;
929 if (n && fwrite(buffer, 1, n, file) != n)
930 {
931 _error->Errno("~LZMAFILE",_("Write error"));
932 break;
933 }
934 if (err == LZMA_STREAM_END)
935 break;
936 }
937 }
938 lzma_end(&stream);
939 fclose(file);
940 }
941 };
942 LZMAFILE* lzma;
943#endif
944 int compressed_fd;
945 pid_t compressor_pid;
946 bool pipe;
947 APT::Configuration::Compressor compressor;
948 unsigned int openmode;
949 unsigned long long seekpos;
950 FileFdPrivate() :
951#ifdef HAVE_ZLIB
952 gz(NULL),
953#endif
954#ifdef HAVE_BZ2
955 bz2(NULL),
956#endif
957#ifdef HAVE_LZMA
958 lzma(NULL),
959#endif
960 compressed_fd(-1), compressor_pid(-1), pipe(false),
961 openmode(0), seekpos(0) {};
962 bool InternalClose(std::string const &FileName)
963 {
964 if (false)
965 /* dummy so that the rest can be 'else if's */;
966#ifdef HAVE_ZLIB
967 else if (gz != NULL) {
968 int const e = gzclose(gz);
969 gz = NULL;
970 // gzdclose() on empty files always fails with "buffer error" here, ignore that
971 if (e != 0 && e != Z_BUF_ERROR)
972 return _error->Errno("close",_("Problem closing the gzip file %s"), FileName.c_str());
973 }
974#endif
975#ifdef HAVE_BZ2
976 else if (bz2 != NULL) {
977 BZ2_bzclose(bz2);
978 bz2 = NULL;
979 }
980#endif
981#ifdef HAVE_LZMA
982 else if (lzma != NULL) {
983 delete lzma;
984 lzma = NULL;
985 }
986#endif
987 return true;
988 }
989 bool CloseDown(std::string const &FileName)
990 {
991 bool const Res = InternalClose(FileName);
992
993 if (compressor_pid > 0)
994 ExecWait(compressor_pid, "FileFdCompressor", true);
995 compressor_pid = -1;
996
997 return Res;
998 }
999 bool InternalStream() const {
1000 return false
1001#ifdef HAVE_BZ2
1002 || bz2 != NULL
1003#endif
1004#ifdef HAVE_LZMA
1005 || lzma != NULL
1006#endif
1007 ;
1008 }
1009
1010
1011 ~FileFdPrivate() { CloseDown(""); }
1012};
1013 /*}}}*/
13d87e2e 1014// FileFd::Open - Open a file /*{{{*/
578bfd0a
AL
1015// ---------------------------------------------------------------------
1016/* The most commonly used open mode combinations are given with Mode */
e5f3f8c1 1017bool FileFd::Open(string FileName,unsigned int const Mode,CompressMode Compress, unsigned long const AccessMode)
578bfd0a 1018{
257e8d66 1019 if (Mode == ReadOnlyGzip)
e5f3f8c1 1020 return Open(FileName, ReadOnly, Gzip, AccessMode);
257e8d66 1021
468720c5 1022 if (Compress == Auto && (Mode & WriteOnly) == WriteOnly)
ae635e3c 1023 return FileFdError("Autodetection on %s only works in ReadOnly openmode!", FileName.c_str());
257e8d66 1024
468720c5
DK
1025 std::vector<APT::Configuration::Compressor> const compressors = APT::Configuration::getCompressors();
1026 std::vector<APT::Configuration::Compressor>::const_iterator compressor = compressors.begin();
1027 if (Compress == Auto)
1028 {
468720c5
DK
1029 for (; compressor != compressors.end(); ++compressor)
1030 {
e788a834 1031 std::string file = FileName + compressor->Extension;
468720c5
DK
1032 if (FileExists(file) == false)
1033 continue;
1034 FileName = file;
468720c5
DK
1035 break;
1036 }
1037 }
1038 else if (Compress == Extension)
1039 {
52b47296
DK
1040 std::string::size_type const found = FileName.find_last_of('.');
1041 std::string ext;
1042 if (found != std::string::npos)
1043 {
1044 ext = FileName.substr(found);
1045 if (ext == ".new" || ext == ".bak")
1046 {
1047 std::string::size_type const found2 = FileName.find_last_of('.', found - 1);
1048 if (found2 != std::string::npos)
1049 ext = FileName.substr(found2, found - found2);
1050 else
1051 ext.clear();
1052 }
1053 }
aee1aac6
DK
1054 for (; compressor != compressors.end(); ++compressor)
1055 if (ext == compressor->Extension)
1056 break;
1057 // no matching extension - assume uncompressed (imagine files like 'example.org_Packages')
1058 if (compressor == compressors.end())
1059 for (compressor = compressors.begin(); compressor != compressors.end(); ++compressor)
1060 if (compressor->Name == ".")
468720c5 1061 break;
468720c5 1062 }
aee1aac6 1063 else
468720c5
DK
1064 {
1065 std::string name;
1066 switch (Compress)
1067 {
aee1aac6 1068 case None: name = "."; break;
468720c5
DK
1069 case Gzip: name = "gzip"; break;
1070 case Bzip2: name = "bzip2"; break;
1071 case Lzma: name = "lzma"; break;
1072 case Xz: name = "xz"; break;
aee1aac6
DK
1073 case Auto:
1074 case Extension:
52b47296 1075 // Unreachable
ae635e3c 1076 return FileFdError("Opening File %s in None, Auto or Extension should be already handled?!?", FileName.c_str());
468720c5
DK
1077 }
1078 for (; compressor != compressors.end(); ++compressor)
1079 if (compressor->Name == name)
1080 break;
aee1aac6 1081 if (compressor == compressors.end())
ae635e3c 1082 return FileFdError("Can't find a configured compressor %s for file %s", name.c_str(), FileName.c_str());
468720c5
DK
1083 }
1084
aee1aac6 1085 if (compressor == compressors.end())
ae635e3c 1086 return FileFdError("Can't find a match for specified compressor mode for file %s", FileName.c_str());
e5f3f8c1 1087 return Open(FileName, Mode, *compressor, AccessMode);
aee1aac6 1088}
e5f3f8c1 1089bool FileFd::Open(string FileName,unsigned int const Mode,APT::Configuration::Compressor const &compressor, unsigned long const AccessMode)
aee1aac6
DK
1090{
1091 Close();
aee1aac6
DK
1092 Flags = AutoClose;
1093
1094 if ((Mode & WriteOnly) != WriteOnly && (Mode & (Atomic | Create | Empty | Exclusive)) != 0)
ae635e3c 1095 return FileFdError("ReadOnly mode for %s doesn't accept additional flags!", FileName.c_str());
aee1aac6 1096 if ((Mode & ReadWrite) == 0)
ae635e3c 1097 return FileFdError("No openmode provided in FileFd::Open for %s", FileName.c_str());
468720c5 1098
257e8d66
DK
1099 if ((Mode & Atomic) == Atomic)
1100 {
1101 Flags |= Replace;
257e8d66
DK
1102 }
1103 else if ((Mode & (Exclusive | Create)) == (Exclusive | Create))
1104 {
1105 // for atomic, this will be done by rename in Close()
1106 unlink(FileName.c_str());
1107 }
1108 if ((Mode & Empty) == Empty)
578bfd0a 1109 {
257e8d66
DK
1110 struct stat Buf;
1111 if (lstat(FileName.c_str(),&Buf) == 0 && S_ISLNK(Buf.st_mode))
1112 unlink(FileName.c_str());
1113 }
c4fc2fd7 1114
561f860a
DK
1115 int fileflags = 0;
1116 #define if_FLAGGED_SET(FLAG, MODE) if ((Mode & FLAG) == FLAG) fileflags |= MODE
1117 if_FLAGGED_SET(ReadWrite, O_RDWR);
1118 else if_FLAGGED_SET(ReadOnly, O_RDONLY);
1119 else if_FLAGGED_SET(WriteOnly, O_WRONLY);
4a9db827 1120
561f860a
DK
1121 if_FLAGGED_SET(Create, O_CREAT);
1122 if_FLAGGED_SET(Empty, O_TRUNC);
1123 if_FLAGGED_SET(Exclusive, O_EXCL);
561f860a 1124 #undef if_FLAGGED_SET
52b47296 1125
7335eebe
AGM
1126 if ((Mode & Atomic) == Atomic)
1127 {
1128 char *name = strdup((FileName + ".XXXXXX").c_str());
1129
dc545c0b 1130 if((iFd = mkstemp(name)) == -1)
7335eebe
AGM
1131 {
1132 free(name);
98b69f9d 1133 return FileFdErrno("mkstemp", "Could not create temporary file for %s", FileName.c_str());
7335eebe
AGM
1134 }
1135
1136 TemporaryFileName = string(name);
7335eebe 1137 free(name);
dc545c0b 1138
230e69d7
DK
1139 // umask() will always set the umask and return the previous value, so
1140 // we first set the umask and then reset it to the old value
1141 mode_t const CurrentUmask = umask(0);
1142 umask(CurrentUmask);
1143 // calculate the actual file permissions (just like open/creat)
1144 mode_t const FilePermissions = (AccessMode & ~CurrentUmask);
1145
1146 if(fchmod(iFd, FilePermissions) == -1)
dc545c0b 1147 return FileFdErrno("fchmod", "Could not change permissions for temporary file %s", TemporaryFileName.c_str());
7335eebe 1148 }
468720c5 1149 else
230e69d7 1150 iFd = open(FileName.c_str(), fileflags, AccessMode);
468720c5 1151
b711c01e 1152 this->FileName = FileName;
561f860a
DK
1153 if (iFd == -1 || OpenInternDescriptor(Mode, compressor) == false)
1154 {
468720c5 1155 if (iFd != -1)
fc81e8f2 1156 {
561f860a
DK
1157 close (iFd);
1158 iFd = -1;
fc81e8f2 1159 }
ae635e3c 1160 return FileFdErrno("open",_("Could not open file %s"), FileName.c_str());
257e8d66 1161 }
578bfd0a 1162
13d87e2e
AL
1163 SetCloseExec(iFd,true);
1164 return true;
578bfd0a 1165}
257e8d66
DK
1166 /*}}}*/
1167// FileFd::OpenDescriptor - Open a filedescriptor /*{{{*/
1168// ---------------------------------------------------------------------
1169/* */
52b47296 1170bool FileFd::OpenDescriptor(int Fd, unsigned int const Mode, CompressMode Compress, bool AutoClose)
aee1aac6
DK
1171{
1172 std::vector<APT::Configuration::Compressor> const compressors = APT::Configuration::getCompressors();
1173 std::vector<APT::Configuration::Compressor>::const_iterator compressor = compressors.begin();
1174 std::string name;
bce778a3
MV
1175
1176 // compat with the old API
1177 if (Mode == ReadOnlyGzip && Compress == None)
1178 Compress = Gzip;
1179
aee1aac6
DK
1180 switch (Compress)
1181 {
1182 case None: name = "."; break;
1183 case Gzip: name = "gzip"; break;
1184 case Bzip2: name = "bzip2"; break;
1185 case Lzma: name = "lzma"; break;
1186 case Xz: name = "xz"; break;
1187 case Auto:
1188 case Extension:
f97bb523
DK
1189 if (AutoClose == true && Fd != -1)
1190 close(Fd);
ae635e3c 1191 return FileFdError("Opening Fd %d in Auto or Extension compression mode is not supported", Fd);
aee1aac6
DK
1192 }
1193 for (; compressor != compressors.end(); ++compressor)
1194 if (compressor->Name == name)
1195 break;
1196 if (compressor == compressors.end())
f97bb523
DK
1197 {
1198 if (AutoClose == true && Fd != -1)
1199 close(Fd);
ae635e3c 1200 return FileFdError("Can't find a configured compressor %s for file %s", name.c_str(), FileName.c_str());
f97bb523 1201 }
aee1aac6
DK
1202 return OpenDescriptor(Fd, Mode, *compressor, AutoClose);
1203}
52b47296 1204bool FileFd::OpenDescriptor(int Fd, unsigned int const Mode, APT::Configuration::Compressor const &compressor, bool AutoClose)
144c0969
JAK
1205{
1206 Close();
1207 Flags = (AutoClose) ? FileFd::AutoClose : 0;
84baaae9 1208 iFd = Fd;
b711c01e 1209 this->FileName = "";
84baaae9 1210 if (OpenInternDescriptor(Mode, compressor) == false)
468720c5 1211 {
f97bb523 1212 if (iFd != -1 && (
84baaae9 1213 (Flags & Compressed) == Compressed ||
f97bb523
DK
1214 AutoClose == true))
1215 {
468720c5 1216 close (iFd);
f97bb523
DK
1217 iFd = -1;
1218 }
1219 return FileFdError(_("Could not open file descriptor %d"), Fd);
144c0969 1220 }
144c0969 1221 return true;
468720c5 1222}
52b47296 1223bool FileFd::OpenInternDescriptor(unsigned int const Mode, APT::Configuration::Compressor const &compressor)
468720c5 1224{
84baaae9
DK
1225 if (iFd == -1)
1226 return false;
ff477ee1
DK
1227 if (compressor.Name == "." || compressor.Binary.empty() == true)
1228 return true;
1229
7f350a37 1230#if defined HAVE_ZLIB || defined HAVE_BZ2 || defined HAVE_LZMA
69d6988a
DK
1231 // the API to open files is similar, so setup to avoid code duplicates later
1232 // and while at it ensure that we close before opening (if its a reopen)
1233 void* (*compress_open)(int, const char *) = NULL;
7f350a37
DK
1234 if (false)
1235 /* dummy so that the rest can be 'else if's */;
4239dbca 1236#define APT_COMPRESS_INIT(NAME,OPEN) \
7f350a37 1237 else if (compressor.Name == NAME) \
69d6988a
DK
1238 { \
1239 compress_open = (void*(*)(int, const char *)) OPEN; \
4239dbca 1240 if (d != NULL) d->InternalClose(FileName); \
69d6988a
DK
1241 }
1242#ifdef HAVE_ZLIB
4239dbca 1243 APT_COMPRESS_INIT("gzip", gzdopen)
69d6988a
DK
1244#endif
1245#ifdef HAVE_BZ2
4239dbca 1246 APT_COMPRESS_INIT("bzip2", BZ2_bzdopen)
69d6988a 1247#endif
7f350a37 1248#ifdef HAVE_LZMA
4239dbca
DK
1249 APT_COMPRESS_INIT("xz", fdopen)
1250 APT_COMPRESS_INIT("lzma", fdopen)
7f350a37 1251#endif
69d6988a
DK
1252#undef APT_COMPRESS_INIT
1253#endif
1254
ba667cf7
DK
1255 if (d == NULL)
1256 {
1257 d = new FileFdPrivate();
1258 d->openmode = Mode;
1259 d->compressor = compressor;
7f350a37 1260#if defined HAVE_ZLIB || defined HAVE_BZ2 || defined HAVE_LZMA
f6ffe501 1261 if ((Flags & AutoClose) != AutoClose && compress_open != NULL)
84baaae9
DK
1262 {
1263 // Need to duplicate fd here or gz/bz2 close for cleanup will close the fd as well
1264 int const internFd = dup(iFd);
1265 if (internFd == -1)
1266 return FileFdErrno("OpenInternDescriptor", _("Could not open file descriptor %d"), iFd);
1267 iFd = internFd;
1268 }
69d6988a 1269#endif
ba667cf7 1270 }
ff477ee1 1271
7f350a37 1272#if defined HAVE_ZLIB || defined HAVE_BZ2 || defined HAVE_LZMA
69d6988a 1273 if (compress_open != NULL)
468720c5 1274 {
69d6988a 1275 void* compress_struct = NULL;
468720c5 1276 if ((Mode & ReadWrite) == ReadWrite)
69d6988a 1277 compress_struct = compress_open(iFd, "r+");
468720c5 1278 else if ((Mode & WriteOnly) == WriteOnly)
69d6988a 1279 compress_struct = compress_open(iFd, "w");
468720c5 1280 else
69d6988a
DK
1281 compress_struct = compress_open(iFd, "r");
1282 if (compress_struct == NULL)
468720c5 1283 return false;
69d6988a 1284
7f350a37
DK
1285 if (false)
1286 /* dummy so that the rest can be 'else if's */;
69d6988a 1287#ifdef HAVE_ZLIB
7f350a37 1288 else if (compressor.Name == "gzip")
69d6988a 1289 d->gz = (gzFile) compress_struct;
699b209e 1290#endif
c4997486 1291#ifdef HAVE_BZ2
7f350a37 1292 else if (compressor.Name == "bzip2")
69d6988a 1293 d->bz2 = (BZFILE*) compress_struct;
7f350a37
DK
1294#endif
1295#ifdef HAVE_LZMA
1296 else if (compressor.Name == "xz" || compressor.Name == "lzma")
adbd7eb4 1297 {
7f350a37
DK
1298 uint32_t const xzlevel = 6;
1299 uint64_t const memlimit = UINT64_MAX;
1300 if (d->lzma == NULL)
1301 d->lzma = new FileFdPrivate::LZMAFILE;
1302 d->lzma->file = (FILE*) compress_struct;
21ea1dbb
MV
1303 lzma_stream tmp_stream = LZMA_STREAM_INIT;
1304 d->lzma->stream = tmp_stream;
7f350a37
DK
1305
1306 if ((Mode & ReadWrite) == ReadWrite)
1307 return FileFdError("ReadWrite mode is not supported for file %s", FileName.c_str());
1308
1309 if ((Mode & WriteOnly) == WriteOnly)
1310 {
1311 if (compressor.Name == "xz")
1312 {
1313 if (lzma_easy_encoder(&d->lzma->stream, xzlevel, LZMA_CHECK_CRC32) != LZMA_OK)
1314 return false;
1315 }
1316 else
1317 {
1318 lzma_options_lzma options;
1319 lzma_lzma_preset(&options, xzlevel);
1320 if (lzma_alone_encoder(&d->lzma->stream, &options) != LZMA_OK)
1321 return false;
1322 }
1323 d->lzma->compressing = true;
1324 }
1325 else
1326 {
1327 if (compressor.Name == "xz")
1328 {
1329 if (lzma_auto_decoder(&d->lzma->stream, memlimit, 0) != LZMA_OK)
1330 return false;
1331 }
1332 else
1333 {
1334 if (lzma_alone_decoder(&d->lzma->stream, memlimit) != LZMA_OK)
1335 return false;
1336 }
1337 d->lzma->compressing = false;
1338 }
adbd7eb4 1339 }
69d6988a 1340#endif
c4997486
DK
1341 Flags |= Compressed;
1342 return true;
1343 }
1344#endif
1345
adbd7eb4
DK
1346 // collect zombies here in case we reopen
1347 if (d->compressor_pid > 0)
1348 ExecWait(d->compressor_pid, "FileFdCompressor", true);
561f860a
DK
1349
1350 if ((Mode & ReadWrite) == ReadWrite)
ae635e3c 1351 return FileFdError("ReadWrite mode is not supported for file %s", FileName.c_str());
561f860a
DK
1352
1353 bool const Comp = (Mode & WriteOnly) == WriteOnly;
561f860a
DK
1354 if (Comp == false)
1355 {
adbd7eb4 1356 // Handle 'decompression' of empty files
561f860a
DK
1357 struct stat Buf;
1358 fstat(iFd, &Buf);
1359 if (Buf.st_size == 0 && S_ISFIFO(Buf.st_mode) == false)
1360 return true;
1361
1362 // We don't need the file open - instead let the compressor open it
1363 // as he properly knows better how to efficiently read from 'his' file
1364 if (FileName.empty() == false)
afb093cd 1365 {
561f860a 1366 close(iFd);
afb093cd
DK
1367 iFd = -1;
1368 }
561f860a
DK
1369 }
1370
1371 // Create a data pipe
1372 int Pipe[2] = {-1,-1};
1373 if (pipe(Pipe) != 0)
ae635e3c 1374 return FileFdErrno("pipe",_("Failed to create subprocess IPC"));
561f860a
DK
1375 for (int J = 0; J != 2; J++)
1376 SetCloseExec(Pipe[J],true);
1377
1378 d->compressed_fd = iFd;
1379 d->pipe = true;
1380
1381 if (Comp == true)
1382 iFd = Pipe[1];
1383 else
1384 iFd = Pipe[0];
1385
1386 // The child..
1387 d->compressor_pid = ExecFork();
1388 if (d->compressor_pid == 0)
1389 {
1390 if (Comp == true)
1391 {
1392 dup2(d->compressed_fd,STDOUT_FILENO);
1393 dup2(Pipe[0],STDIN_FILENO);
1394 }
1395 else
1396 {
7f350a37 1397 if (d->compressed_fd != -1)
561f860a
DK
1398 dup2(d->compressed_fd,STDIN_FILENO);
1399 dup2(Pipe[1],STDOUT_FILENO);
1400 }
0b4895d3
DK
1401 int const nullfd = open("/dev/null", O_WRONLY);
1402 if (nullfd != -1)
1403 {
1404 dup2(nullfd,STDERR_FILENO);
1405 close(nullfd);
1406 }
561f860a
DK
1407
1408 SetCloseExec(STDOUT_FILENO,false);
1409 SetCloseExec(STDIN_FILENO,false);
1410
1411 std::vector<char const*> Args;
1412 Args.push_back(compressor.Binary.c_str());
1413 std::vector<std::string> const * const addArgs =
1414 (Comp == true) ? &(compressor.CompressArgs) : &(compressor.UncompressArgs);
1415 for (std::vector<std::string>::const_iterator a = addArgs->begin();
1416 a != addArgs->end(); ++a)
1417 Args.push_back(a->c_str());
1418 if (Comp == false && FileName.empty() == false)
1419 {
bb93178b
DK
1420 // commands not needing arguments, do not need to be told about using standard output
1421 // in reality, only testcases with tools like cat, rev, rot13, … are able to trigger this
1422 if (compressor.CompressArgs.empty() == false && compressor.UncompressArgs.empty() == false)
1423 Args.push_back("--stdout");
561f860a
DK
1424 if (TemporaryFileName.empty() == false)
1425 Args.push_back(TemporaryFileName.c_str());
1426 else
1427 Args.push_back(FileName.c_str());
1428 }
1429 Args.push_back(NULL);
1430
1431 execvp(Args[0],(char **)&Args[0]);
1432 cerr << _("Failed to exec compressor ") << Args[0] << endl;
1433 _exit(100);
1434 }
1435 if (Comp == true)
1436 close(Pipe[0]);
468720c5 1437 else
561f860a 1438 close(Pipe[1]);
561f860a 1439
468720c5 1440 return true;
144c0969 1441}
578bfd0a 1442 /*}}}*/
8e06abb2 1443// FileFd::~File - Closes the file /*{{{*/
578bfd0a
AL
1444// ---------------------------------------------------------------------
1445/* If the proper modes are selected then we close the Fd and possibly
1446 unlink the file on error. */
8e06abb2 1447FileFd::~FileFd()
578bfd0a
AL
1448{
1449 Close();
500400fe 1450 if (d != NULL)
500400fe 1451 d->CloseDown(FileName);
96ab3c6f
MV
1452 delete d;
1453 d = NULL;
578bfd0a
AL
1454}
1455 /*}}}*/
8e06abb2 1456// FileFd::Read - Read a bit of the file /*{{{*/
578bfd0a 1457// ---------------------------------------------------------------------
1e3f4083 1458/* We are careful to handle interruption by a signal while reading
b0db36b1 1459 gracefully. */
650faab0 1460bool FileFd::Read(void *To,unsigned long long Size,unsigned long long *Actual)
578bfd0a 1461{
3286ad13 1462 ssize_t Res;
b0db36b1 1463 errno = 0;
f604cf55
AL
1464 if (Actual != 0)
1465 *Actual = 0;
699b209e 1466 *((char *)To) = '\0';
b0db36b1 1467 do
578bfd0a 1468 {
7f350a37
DK
1469 if (false)
1470 /* dummy so that the rest can be 'else if's */;
7efb8c8e 1471#ifdef HAVE_ZLIB
7f350a37 1472 else if (d != NULL && d->gz != NULL)
c4997486 1473 Res = gzread(d->gz,To,Size);
c4997486
DK
1474#endif
1475#ifdef HAVE_BZ2
7f350a37 1476 else if (d != NULL && d->bz2 != NULL)
c4997486 1477 Res = BZ2_bzread(d->bz2,To,Size);
699b209e 1478#endif
7f350a37
DK
1479#ifdef HAVE_LZMA
1480 else if (d != NULL && d->lzma != NULL)
1481 {
1482 if (d->lzma->eof == true)
1483 break;
1484
1485 d->lzma->stream.next_out = (uint8_t *) To;
1486 d->lzma->stream.avail_out = Size;
1487 if (d->lzma->stream.avail_in == 0)
1488 {
1489 d->lzma->stream.next_in = d->lzma->buffer;
1490 d->lzma->stream.avail_in = fread(d->lzma->buffer, 1, sizeof(d->lzma->buffer)/sizeof(d->lzma->buffer[0]), d->lzma->file);
1491 }
1492 d->lzma->err = lzma_code(&d->lzma->stream, LZMA_RUN);
1493 if (d->lzma->err == LZMA_STREAM_END)
1494 {
1495 d->lzma->eof = true;
1496 Res = Size - d->lzma->stream.avail_out;
1497 }
1498 else if (d->lzma->err != LZMA_OK)
1499 {
1500 Res = -1;
1501 errno = 0;
1502 }
1503 else
c4b113e6 1504 {
7f350a37 1505 Res = Size - d->lzma->stream.avail_out;
c4b113e6
DK
1506 if (Res == 0)
1507 {
1508 // lzma run was okay, but produced no output…
1509 Res = -1;
1510 errno = EINTR;
1511 }
1512 }
7f350a37
DK
1513 }
1514#endif
1515 else
a3a03f5d 1516 Res = read(iFd,To,Size);
b711c01e 1517
b0db36b1
AL
1518 if (Res < 0)
1519 {
b711c01e 1520 if (errno == EINTR)
c4b113e6
DK
1521 {
1522 // trick the while-loop into running again
1523 Res = 1;
1524 errno = 0;
b711c01e 1525 continue;
c4b113e6 1526 }
7f350a37
DK
1527 if (false)
1528 /* dummy so that the rest can be 'else if's */;
7efb8c8e 1529#ifdef HAVE_ZLIB
7f350a37 1530 else if (d != NULL && d->gz != NULL)
b711c01e
DK
1531 {
1532 int err;
1533 char const * const errmsg = gzerror(d->gz, &err);
1534 if (err != Z_ERRNO)
ae635e3c 1535 return FileFdError("gzread: %s (%d: %s)", _("Read error"), err, errmsg);
b711c01e 1536 }
c4997486
DK
1537#endif
1538#ifdef HAVE_BZ2
7f350a37 1539 else if (d != NULL && d->bz2 != NULL)
c4997486
DK
1540 {
1541 int err;
1542 char const * const errmsg = BZ2_bzerror(d->bz2, &err);
1543 if (err != BZ_IO_ERROR)
c36db2b5 1544 return FileFdError("BZ2_bzread: %s %s (%d: %s)", FileName.c_str(), _("Read error"), err, errmsg);
c4997486 1545 }
7f350a37
DK
1546#endif
1547#ifdef HAVE_LZMA
1548 else if (d != NULL && d->lzma != NULL)
1549 return FileFdError("lzma_read: %s (%d)", _("Read error"), d->lzma->err);
b711c01e 1550#endif
ae635e3c 1551 return FileFdErrno("read",_("Read error"));
b0db36b1 1552 }
578bfd0a 1553
b0db36b1
AL
1554 To = (char *)To + Res;
1555 Size -= Res;
ff477ee1
DK
1556 if (d != NULL)
1557 d->seekpos += Res;
f604cf55
AL
1558 if (Actual != 0)
1559 *Actual += Res;
b0db36b1
AL
1560 }
1561 while (Res > 0 && Size > 0);
1562
1563 if (Size == 0)
1564 return true;
1565
ddc1d8d0 1566 // Eof handling
f604cf55 1567 if (Actual != 0)
ddc1d8d0
AL
1568 {
1569 Flags |= HitEof;
1570 return true;
1571 }
ae635e3c
DK
1572
1573 return FileFdError(_("read, still have %llu to read but none left"), Size);
578bfd0a
AL
1574}
1575 /*}}}*/
032bd56f
DK
1576// FileFd::ReadLine - Read a complete line from the file /*{{{*/
1577// ---------------------------------------------------------------------
1578/* Beware: This method can be quiet slow for big buffers on UNcompressed
1579 files because of the naive implementation! */
1580char* FileFd::ReadLine(char *To, unsigned long long const Size)
1581{
699b209e 1582 *To = '\0';
7efb8c8e 1583#ifdef HAVE_ZLIB
ff477ee1 1584 if (d != NULL && d->gz != NULL)
032bd56f 1585 return gzgets(d->gz, To, Size);
699b209e 1586#endif
032bd56f
DK
1587
1588 unsigned long long read = 0;
40468850
DK
1589 while ((Size - 1) != read)
1590 {
1591 unsigned long long done = 0;
1592 if (Read(To + read, 1, &done) == false)
1593 return NULL;
1594 if (done == 0)
1595 break;
1596 if (To[read++] == '\n')
1597 break;
1598 }
1599 if (read == 0)
032bd56f 1600 return NULL;
40468850 1601 To[read] = '\0';
032bd56f
DK
1602 return To;
1603}
1604 /*}}}*/
8e06abb2 1605// FileFd::Write - Write to the file /*{{{*/
578bfd0a
AL
1606// ---------------------------------------------------------------------
1607/* */
650faab0 1608bool FileFd::Write(const void *From,unsigned long long Size)
578bfd0a 1609{
3286ad13 1610 ssize_t Res;
b0db36b1
AL
1611 errno = 0;
1612 do
578bfd0a 1613 {
7f350a37
DK
1614 if (false)
1615 /* dummy so that the rest can be 'else if's */;
7efb8c8e 1616#ifdef HAVE_ZLIB
7f350a37
DK
1617 else if (d != NULL && d->gz != NULL)
1618 Res = gzwrite(d->gz,From,Size);
c4997486
DK
1619#endif
1620#ifdef HAVE_BZ2
7f350a37
DK
1621 else if (d != NULL && d->bz2 != NULL)
1622 Res = BZ2_bzwrite(d->bz2,(void*)From,Size);
699b209e 1623#endif
7f350a37
DK
1624#ifdef HAVE_LZMA
1625 else if (d != NULL && d->lzma != NULL)
1626 {
1627 d->lzma->stream.next_in = (uint8_t *)From;
1628 d->lzma->stream.avail_in = Size;
1629 d->lzma->stream.next_out = d->lzma->buffer;
1630 d->lzma->stream.avail_out = sizeof(d->lzma->buffer)/sizeof(d->lzma->buffer[0]);
1631 d->lzma->err = lzma_code(&d->lzma->stream, LZMA_RUN);
1632 if (d->lzma->err != LZMA_OK)
1633 return false;
1634 size_t const n = sizeof(d->lzma->buffer)/sizeof(d->lzma->buffer[0]) - d->lzma->stream.avail_out;
1635 size_t const m = (n == 0) ? 0 : fwrite(d->lzma->buffer, 1, n, d->lzma->file);
1636 if (m != n)
1637 Res = -1;
1638 else
1639 Res = Size - d->lzma->stream.avail_in;
1640 }
699b209e 1641#endif
7f350a37
DK
1642 else
1643 Res = write(iFd,From,Size);
1644
b0db36b1
AL
1645 if (Res < 0 && errno == EINTR)
1646 continue;
1647 if (Res < 0)
1648 {
7f350a37
DK
1649 if (false)
1650 /* dummy so that the rest can be 'else if's */;
c4997486 1651#ifdef HAVE_ZLIB
7f350a37 1652 else if (d != NULL && d->gz != NULL)
c4997486
DK
1653 {
1654 int err;
1655 char const * const errmsg = gzerror(d->gz, &err);
1656 if (err != Z_ERRNO)
ae635e3c 1657 return FileFdError("gzwrite: %s (%d: %s)", _("Write error"), err, errmsg);
c4997486
DK
1658 }
1659#endif
1660#ifdef HAVE_BZ2
7f350a37 1661 else if (d != NULL && d->bz2 != NULL)
c4997486
DK
1662 {
1663 int err;
1664 char const * const errmsg = BZ2_bzerror(d->bz2, &err);
1665 if (err != BZ_IO_ERROR)
ae635e3c 1666 return FileFdError("BZ2_bzwrite: %s (%d: %s)", _("Write error"), err, errmsg);
c4997486 1667 }
7f350a37
DK
1668#endif
1669#ifdef HAVE_LZMA
1670 else if (d != NULL && d->lzma != NULL)
1671 return FileFdErrno("lzma_fwrite", _("Write error"));
c4997486 1672#endif
ae635e3c 1673 return FileFdErrno("write",_("Write error"));
b0db36b1
AL
1674 }
1675
cf4ff3b7 1676 From = (char const *)From + Res;
b0db36b1 1677 Size -= Res;
ff477ee1
DK
1678 if (d != NULL)
1679 d->seekpos += Res;
578bfd0a 1680 }
b0db36b1 1681 while (Res > 0 && Size > 0);
578bfd0a 1682
b0db36b1
AL
1683 if (Size == 0)
1684 return true;
ae635e3c
DK
1685
1686 return FileFdError(_("write, still have %llu to write but couldn't"), Size);
d68d65ad
DK
1687}
1688bool FileFd::Write(int Fd, const void *From, unsigned long long Size)
1689{
3286ad13 1690 ssize_t Res;
d68d65ad
DK
1691 errno = 0;
1692 do
1693 {
1694 Res = write(Fd,From,Size);
1695 if (Res < 0 && errno == EINTR)
1696 continue;
1697 if (Res < 0)
1698 return _error->Errno("write",_("Write error"));
1699
cf4ff3b7 1700 From = (char const *)From + Res;
d68d65ad
DK
1701 Size -= Res;
1702 }
1703 while (Res > 0 && Size > 0);
1704
1705 if (Size == 0)
1706 return true;
1707
1708 return _error->Error(_("write, still have %llu to write but couldn't"), Size);
578bfd0a
AL
1709}
1710 /*}}}*/
8e06abb2 1711// FileFd::Seek - Seek in the file /*{{{*/
578bfd0a
AL
1712// ---------------------------------------------------------------------
1713/* */
650faab0 1714bool FileFd::Seek(unsigned long long To)
578bfd0a 1715{
bb93178b
DK
1716 Flags &= ~HitEof;
1717
4239dbca 1718 if (d != NULL && (d->pipe == true || d->InternalStream() == true))
699b209e 1719 {
1abbc47c
DK
1720 // Our poor man seeking in pipes is costly, so try to avoid it
1721 unsigned long long seekpos = Tell();
1722 if (seekpos == To)
1723 return true;
1724 else if (seekpos < To)
1725 return Skip(To - seekpos);
1726
561f860a 1727 if ((d->openmode & ReadOnly) != ReadOnly)
ae635e3c 1728 return FileFdError("Reopen is only implemented for read-only files!");
4239dbca 1729 d->InternalClose(FileName);
afb093cd
DK
1730 if (iFd != -1)
1731 close(iFd);
1732 iFd = -1;
561f860a
DK
1733 if (TemporaryFileName.empty() == false)
1734 iFd = open(TemporaryFileName.c_str(), O_RDONLY);
1735 else if (FileName.empty() == false)
1736 iFd = open(FileName.c_str(), O_RDONLY);
1737 else
6fd947bd
DK
1738 {
1739 if (d->compressed_fd > 0)
1740 if (lseek(d->compressed_fd, 0, SEEK_SET) != 0)
1741 iFd = d->compressed_fd;
500400fe 1742 if (iFd < 0)
ae635e3c 1743 return FileFdError("Reopen is not implemented for pipes opened with FileFd::OpenDescriptor()!");
6fd947bd 1744 }
561f860a
DK
1745
1746 if (OpenInternDescriptor(d->openmode, d->compressor) == false)
ae635e3c 1747 return FileFdError("Seek on file %s because it couldn't be reopened", FileName.c_str());
561f860a
DK
1748
1749 if (To != 0)
1750 return Skip(To);
1abbc47c
DK
1751
1752 d->seekpos = To;
561f860a 1753 return true;
699b209e 1754 }
3286ad13 1755 off_t res;
7efb8c8e 1756#ifdef HAVE_ZLIB
ff477ee1 1757 if (d != NULL && d->gz)
032bd56f 1758 res = gzseek(d->gz,To,SEEK_SET);
a3a03f5d 1759 else
699b209e 1760#endif
a3a03f5d 1761 res = lseek(iFd,To,SEEK_SET);
3286ad13 1762 if (res != (off_t)To)
ae635e3c 1763 return FileFdError("Unable to seek to %llu", To);
1abbc47c 1764
ff477ee1
DK
1765 if (d != NULL)
1766 d->seekpos = To;
727f18af
AL
1767 return true;
1768}
1769 /*}}}*/
1770// FileFd::Skip - Seek in the file /*{{{*/
1771// ---------------------------------------------------------------------
1772/* */
650faab0 1773bool FileFd::Skip(unsigned long long Over)
727f18af 1774{
4239dbca 1775 if (d != NULL && (d->pipe == true || d->InternalStream() == true))
40468850 1776 {
40468850
DK
1777 char buffer[1024];
1778 while (Over != 0)
1779 {
1780 unsigned long long toread = std::min((unsigned long long) sizeof(buffer), Over);
1781 if (Read(buffer, toread) == false)
ae635e3c 1782 return FileFdError("Unable to seek ahead %llu",Over);
40468850
DK
1783 Over -= toread;
1784 }
1785 return true;
1786 }
1787
3286ad13 1788 off_t res;
7efb8c8e 1789#ifdef HAVE_ZLIB
ff477ee1 1790 if (d != NULL && d->gz != NULL)
032bd56f 1791 res = gzseek(d->gz,Over,SEEK_CUR);
a3a03f5d 1792 else
699b209e 1793#endif
a3a03f5d 1794 res = lseek(iFd,Over,SEEK_CUR);
1795 if (res < 0)
ae635e3c 1796 return FileFdError("Unable to seek ahead %llu",Over);
ff477ee1
DK
1797 if (d != NULL)
1798 d->seekpos = res;
1abbc47c 1799
6d5dd02a
AL
1800 return true;
1801}
1802 /*}}}*/
1803// FileFd::Truncate - Truncate the file /*{{{*/
1804// ---------------------------------------------------------------------
1805/* */
650faab0 1806bool FileFd::Truncate(unsigned long long To)
6d5dd02a 1807{
ad5051ef
DK
1808 // truncating /dev/null is always successful - as we get an error otherwise
1809 if (To == 0 && FileName == "/dev/null")
1810 return true;
7f350a37 1811#if defined HAVE_ZLIB || defined HAVE_BZ2 || defined HAVE_LZMA
4239dbca 1812 if (d != NULL && (d->InternalStream() == true
7f350a37 1813#ifdef HAVE_ZLIB
4239dbca 1814 || d->gz != NULL
7f350a37 1815#endif
4239dbca 1816 ))
ae635e3c 1817 return FileFdError("Truncating compressed files is not implemented (%s)", FileName.c_str());
c4997486 1818#endif
6d5dd02a 1819 if (ftruncate(iFd,To) != 0)
ae635e3c
DK
1820 return FileFdError("Unable to truncate to %llu",To);
1821
578bfd0a
AL
1822 return true;
1823}
1824 /*}}}*/
7f25bdff
AL
1825// FileFd::Tell - Current seek position /*{{{*/
1826// ---------------------------------------------------------------------
1827/* */
650faab0 1828unsigned long long FileFd::Tell()
7f25bdff 1829{
1abbc47c
DK
1830 // In theory, we could just return seekpos here always instead of
1831 // seeking around, but not all users of FileFd use always Seek() and co
1832 // so d->seekpos isn't always true and we can just use it as a hint if
1833 // we have nothing else, but not always as an authority…
4239dbca 1834 if (d != NULL && (d->pipe == true || d->InternalStream() == true))
1abbc47c
DK
1835 return d->seekpos;
1836
a3a03f5d 1837 off_t Res;
7efb8c8e 1838#ifdef HAVE_ZLIB
ff477ee1 1839 if (d != NULL && d->gz != NULL)
032bd56f 1840 Res = gztell(d->gz);
a3a03f5d 1841 else
699b209e 1842#endif
a3a03f5d 1843 Res = lseek(iFd,0,SEEK_CUR);
7f25bdff 1844 if (Res == (off_t)-1)
ae635e3c 1845 FileFdErrno("lseek","Failed to determine the current file position");
ff477ee1
DK
1846 if (d != NULL)
1847 d->seekpos = Res;
7f25bdff
AL
1848 return Res;
1849}
1850 /*}}}*/
8190b07a 1851static bool StatFileFd(char const * const msg, int const iFd, std::string const &FileName, struct stat &Buf, FileFdPrivate * const d) /*{{{*/
578bfd0a 1852{
6008b79a
DK
1853 bool ispipe = (d != NULL && d->pipe == true);
1854 if (ispipe == false)
1855 {
1856 if (fstat(iFd,&Buf) != 0)
8190b07a
DK
1857 // higher-level code will generate more meaningful messages,
1858 // even translated this would be meaningless for users
1859 return _error->Errno("fstat", "Unable to determine %s for fd %i", msg, iFd);
003c40d3
DK
1860 if (FileName.empty() == false)
1861 ispipe = S_ISFIFO(Buf.st_mode);
6008b79a 1862 }
699b209e
DK
1863
1864 // for compressor pipes st_size is undefined and at 'best' zero
6008b79a 1865 if (ispipe == true)
699b209e
DK
1866 {
1867 // we set it here, too, as we get the info here for free
1868 // in theory the Open-methods should take care of it already
ff477ee1
DK
1869 if (d != NULL)
1870 d->pipe = true;
699b209e 1871 if (stat(FileName.c_str(), &Buf) != 0)
8190b07a
DK
1872 return _error->Errno("fstat", "Unable to determine %s for file %s", msg, FileName.c_str());
1873 }
1874 return true;
1875}
1876 /*}}}*/
1877// FileFd::FileSize - Return the size of the file /*{{{*/
1878unsigned long long FileFd::FileSize()
1879{
1880 struct stat Buf;
1881 if (StatFileFd("file size", iFd, FileName, Buf, d) == false)
1882 {
1883 Flags |= Fail;
1884 return 0;
699b209e 1885 }
4260fd39
DK
1886 return Buf.st_size;
1887}
1888 /*}}}*/
8190b07a
DK
1889// FileFd::ModificationTime - Return the time of last touch /*{{{*/
1890time_t FileFd::ModificationTime()
1891{
1892 struct stat Buf;
1893 if (StatFileFd("modification time", iFd, FileName, Buf, d) == false)
1894 {
1895 Flags |= Fail;
1896 return 0;
1897 }
1898 return Buf.st_mtime;
1899}
1900 /*}}}*/
4260fd39
DK
1901// FileFd::Size - Return the size of the content in the file /*{{{*/
1902// ---------------------------------------------------------------------
1903/* */
650faab0 1904unsigned long long FileFd::Size()
4260fd39 1905{
650faab0 1906 unsigned long long size = FileSize();
44dc669e 1907
699b209e
DK
1908 // for compressor pipes st_size is undefined and at 'best' zero,
1909 // so we 'read' the content and 'seek' back - see there
4239dbca 1910 if (d != NULL && (d->pipe == true || (d->InternalStream() == true && size > 0)))
699b209e 1911 {
1abbc47c 1912 unsigned long long const oldSeek = Tell();
699b209e
DK
1913 char ignore[1000];
1914 unsigned long long read = 0;
1915 do {
638593bd
DK
1916 if (Read(ignore, sizeof(ignore), &read) == false)
1917 {
1918 Seek(oldSeek);
1919 return 0;
1920 }
699b209e 1921 } while(read != 0);
1abbc47c
DK
1922 size = Tell();
1923 Seek(oldSeek);
699b209e 1924 }
7efb8c8e 1925#ifdef HAVE_ZLIB
44dc669e 1926 // only check gzsize if we are actually a gzip file, just checking for
032bd56f 1927 // "gz" is not sufficient as uncompressed files could be opened with
44dc669e 1928 // gzopen in "direct" mode as well
ff477ee1 1929 else if (d != NULL && d->gz && !gzdirect(d->gz) && size > 0)
9c182afa 1930 {
65c72a4b 1931 off_t const oldPos = lseek(iFd,0,SEEK_CUR);
9c182afa
MP
1932 /* unfortunately zlib.h doesn't provide a gzsize(), so we have to do
1933 * this ourselves; the original (uncompressed) file size is the last 32
1934 * bits of the file */
650faab0 1935 // FIXME: Size for gz-files is limited by 32bit… no largefile support
9c182afa 1936 if (lseek(iFd, -4, SEEK_END) < 0)
fbb89d94 1937 {
638593bd
DK
1938 FileFdErrno("lseek","Unable to seek to end of gzipped file");
1939 return 0;
fbb89d94 1940 }
05eab8af 1941 uint32_t size = 0;
9c182afa 1942 if (read(iFd, &size, 4) != 4)
fbb89d94 1943 {
638593bd
DK
1944 FileFdErrno("read","Unable to read original size of gzipped file");
1945 return 0;
fbb89d94 1946 }
05eab8af 1947 size = le32toh(size);
9c182afa 1948
65c72a4b 1949 if (lseek(iFd, oldPos, SEEK_SET) < 0)
fbb89d94 1950 {
638593bd
DK
1951 FileFdErrno("lseek","Unable to seek in gzipped file");
1952 return 0;
fbb89d94 1953 }
65c72a4b 1954
9c182afa
MP
1955 return size;
1956 }
699b209e 1957#endif
9c182afa 1958
44dc669e 1959 return size;
578bfd0a
AL
1960}
1961 /*}}}*/
8e06abb2 1962// FileFd::Close - Close the file if the close flag is set /*{{{*/
578bfd0a
AL
1963// ---------------------------------------------------------------------
1964/* */
8e06abb2 1965bool FileFd::Close()
578bfd0a 1966{
032bd56f
DK
1967 if (iFd == -1)
1968 return true;
1969
578bfd0a
AL
1970 bool Res = true;
1971 if ((Flags & AutoClose) == AutoClose)
d13c2d3f 1972 {
500400fe
DK
1973 if ((Flags & Compressed) != Compressed && iFd > 0 && close(iFd) != 0)
1974 Res &= _error->Errno("close",_("Problem closing the file %s"), FileName.c_str());
500400fe
DK
1975 if (d != NULL)
1976 {
1977 Res &= d->CloseDown(FileName);
1978 delete d;
1979 d = NULL;
1980 }
d13c2d3f 1981 }
3010fb0e 1982
d3aac32e 1983 if ((Flags & Replace) == Replace) {
3010fb0e 1984 if (rename(TemporaryFileName.c_str(), FileName.c_str()) != 0)
62d073d9
DK
1985 Res &= _error->Errno("rename",_("Problem renaming the file %s to %s"), TemporaryFileName.c_str(), FileName.c_str());
1986
fd3b761e 1987 FileName = TemporaryFileName; // for the unlink() below.
257e8d66 1988 TemporaryFileName.clear();
3010fb0e 1989 }
62d073d9
DK
1990
1991 iFd = -1;
1992
578bfd0a
AL
1993 if ((Flags & Fail) == Fail && (Flags & DelOnFail) == DelOnFail &&
1994 FileName.empty() == false)
1995 if (unlink(FileName.c_str()) != 0)
62d073d9 1996 Res &= _error->WarningE("unlnk",_("Problem unlinking the file %s"), FileName.c_str());
3010fb0e 1997
fbb89d94
DK
1998 if (Res == false)
1999 Flags |= Fail;
578bfd0a
AL
2000 return Res;
2001}
2002 /*}}}*/
b2e465d6
AL
2003// FileFd::Sync - Sync the file /*{{{*/
2004// ---------------------------------------------------------------------
2005/* */
2006bool FileFd::Sync()
2007{
b2e465d6 2008 if (fsync(iFd) != 0)
ae635e3c
DK
2009 return FileFdErrno("sync",_("Problem syncing the file"));
2010 return true;
2011}
2012 /*}}}*/
2013// FileFd::FileFdErrno - set Fail and call _error->Errno *{{{*/
2014bool FileFd::FileFdErrno(const char *Function, const char *Description,...)
2015{
2016 Flags |= Fail;
2017 va_list args;
2018 size_t msgSize = 400;
2019 int const errsv = errno;
2020 while (true)
fbb89d94 2021 {
ae635e3c
DK
2022 va_start(args,Description);
2023 if (_error->InsertErrno(GlobalError::ERROR, Function, Description, args, errsv, msgSize) == false)
2024 break;
2025 va_end(args);
fbb89d94 2026 }
ae635e3c
DK
2027 return false;
2028}
2029 /*}}}*/
2030// FileFd::FileFdError - set Fail and call _error->Error *{{{*/
2031bool FileFd::FileFdError(const char *Description,...) {
2032 Flags |= Fail;
2033 va_list args;
2034 size_t msgSize = 400;
2035 while (true)
2036 {
2037 va_start(args,Description);
2038 if (_error->Insert(GlobalError::ERROR, Description, args, msgSize) == false)
2039 break;
2040 va_end(args);
2041 }
2042 return false;
b2e465d6
AL
2043}
2044 /*}}}*/
699b209e 2045
7f350a37
DK
2046APT_DEPRECATED gzFile FileFd::gzFd() {
2047#ifdef HAVE_ZLIB
2048 return d->gz;
2049#else
2050 return NULL;
2051#endif
2052}
8d01b9d6 2053
f8aba23f 2054// Glob - wrapper around "glob()" /*{{{*/
8d01b9d6
MV
2055std::vector<std::string> Glob(std::string const &pattern, int flags)
2056{
2057 std::vector<std::string> result;
2058 glob_t globbuf;
ec4835a1
ÁGM
2059 int glob_res;
2060 unsigned int i;
8d01b9d6
MV
2061
2062 glob_res = glob(pattern.c_str(), flags, NULL, &globbuf);
2063
2064 if (glob_res != 0)
2065 {
2066 if(glob_res != GLOB_NOMATCH) {
2067 _error->Errno("glob", "Problem with glob");
2068 return result;
2069 }
2070 }
2071
2072 // append results
2073 for(i=0;i<globbuf.gl_pathc;i++)
2074 result.push_back(string(globbuf.gl_pathv[i]));
2075
2076 globfree(&globbuf);
2077 return result;
2078}
2079 /*}}}*/
f8aba23f 2080std::string GetTempDir() /*{{{*/
68e01721
MV
2081{
2082 const char *tmpdir = getenv("TMPDIR");
2083
2084#ifdef P_tmpdir
2085 if (!tmpdir)
2086 tmpdir = P_tmpdir;
2087#endif
2088
68e01721 2089 struct stat st;
0d303f17
DK
2090 if (!tmpdir || strlen(tmpdir) == 0 || // tmpdir is set
2091 stat(tmpdir, &st) != 0 || (st.st_mode & S_IFDIR) == 0 || // exists and is directory
2092 access(tmpdir, R_OK | W_OK | X_OK) != 0 // current user has rwx access to directory
2093 )
68e01721
MV
2094 tmpdir = "/tmp";
2095
2096 return string(tmpdir);
2097}
f8aba23f
DK
2098 /*}}}*/
2099FileFd* GetTempFile(std::string const &Prefix, bool ImmediateUnlink) /*{{{*/
0d29b9d4
MV
2100{
2101 char fn[512];
2102 FileFd *Fd = new FileFd();
2103
2104 std::string tempdir = GetTempDir();
2105 snprintf(fn, sizeof(fn), "%s/%s.XXXXXX",
2106 tempdir.c_str(), Prefix.c_str());
2107 int fd = mkstemp(fn);
2108 if(ImmediateUnlink)
2109 unlink(fn);
2110 if (fd < 0)
2111 {
2112 _error->Errno("GetTempFile",_("Unable to mkstemp %s"), fn);
2113 return NULL;
2114 }
2115 if (!Fd->OpenDescriptor(fd, FileFd::WriteOnly, FileFd::None, true))
2116 {
2117 _error->Errno("GetTempFile",_("Unable to write to %s"),fn);
2118 return NULL;
2119 }
2120
2121 return Fd;
2122}
f8aba23f
DK
2123 /*}}}*/
2124bool Rename(std::string From, std::string To) /*{{{*/
c1409d1b
MV
2125{
2126 if (rename(From.c_str(),To.c_str()) != 0)
2127 {
2128 _error->Error(_("rename failed, %s (%s -> %s)."),strerror(errno),
2129 From.c_str(),To.c_str());
2130 return false;
f8aba23f 2131 }
c1409d1b
MV
2132 return true;
2133}
f8aba23f
DK
2134 /*}}}*/
2135bool Popen(const char* Args[], FileFd &Fd, pid_t &Child, FileFd::OpenMode Mode)/*{{{*/
7ad2a347
MV
2136{
2137 int fd;
2138 if (Mode != FileFd::ReadOnly && Mode != FileFd::WriteOnly)
2139 return _error->Error("Popen supports ReadOnly (x)or WriteOnly mode only");
2140
2141 int Pipe[2] = {-1, -1};
2142 if(pipe(Pipe) != 0)
7ad2a347 2143 return _error->Errno("pipe", _("Failed to create subprocess IPC"));
5e49cbb7 2144
7ad2a347
MV
2145 std::set<int> keep_fds;
2146 keep_fds.insert(Pipe[0]);
2147 keep_fds.insert(Pipe[1]);
2148 Child = ExecFork(keep_fds);
2149 if(Child < 0)
2150 return _error->Errno("fork", "Failed to fork");
2151 if(Child == 0)
2152 {
2153 if(Mode == FileFd::ReadOnly)
2154 {
2155 close(Pipe[0]);
2156 fd = Pipe[1];
2157 }
2158 else if(Mode == FileFd::WriteOnly)
2159 {
2160 close(Pipe[1]);
2161 fd = Pipe[0];
2162 }
2163
2164 if(Mode == FileFd::ReadOnly)
2165 {
2166 dup2(fd, 1);
2167 dup2(fd, 2);
2168 } else if(Mode == FileFd::WriteOnly)
2169 dup2(fd, 0);
2170
2171 execv(Args[0], (char**)Args);
2172 _exit(100);
2173 }
2174 if(Mode == FileFd::ReadOnly)
2175 {
2176 close(Pipe[1]);
2177 fd = Pipe[0];
2178 } else if(Mode == FileFd::WriteOnly)
2179 {
2180 close(Pipe[0]);
2181 fd = Pipe[1];
2182 }
2183 Fd.OpenDescriptor(fd, Mode, FileFd::None, true);
2184
2185 return true;
2186}
f8aba23f
DK
2187 /*}}}*/
2188bool DropPrivileges() /*{{{*/
fc1a78d8 2189{
8f45798d
DK
2190 if(_config->FindB("Debug::NoDropPrivs", false) == true)
2191 return true;
2192
2193#if __gnu_linux__
2194#if defined(PR_SET_NO_NEW_PRIVS) && ( PR_SET_NO_NEW_PRIVS != 38 )
2195#error "PR_SET_NO_NEW_PRIVS is defined, but with a different value than expected!"
2196#endif
2197 // see prctl(2), needs linux3.5 at runtime - magic constant to avoid it at buildtime
2198 int ret = prctl(38, 1, 0, 0, 0);
2199 // ignore EINVAL - kernel is too old to understand the option
2200 if(ret < 0 && errno != EINVAL)
2201 _error->Warning("PR_SET_NO_NEW_PRIVS failed with %i", ret);
2202#endif
2203
990dd78a
DK
2204 // empty setting disables privilege dropping - this also ensures
2205 // backward compatibility, see bug #764506
2206 const std::string toUser = _config->Find("APT::Sandbox::User");
2207 if (toUser.empty())
2208 return true;
2209
f1e3c8f0 2210 // uid will be 0 in the end, but gid might be different anyway
8f45798d
DK
2211 uid_t const old_uid = getuid();
2212 gid_t const old_gid = getgid();
fc1a78d8 2213
5f2047ec
JAK
2214 if (old_uid != 0)
2215 return true;
3927c6da 2216
b8dae9a1 2217 struct passwd *pw = getpwnam(toUser.c_str());
fc1a78d8 2218 if (pw == NULL)
b8dae9a1 2219 return _error->Error("No user %s, can not drop rights", toUser.c_str());
3927c6da 2220
f1e3c8f0 2221 // Do not change the order here, it might break things
3b084f06 2222 if (setgroups(1, &pw->pw_gid))
3927c6da
MV
2223 return _error->Errno("setgroups", "Failed to setgroups");
2224
2225 if (setegid(pw->pw_gid) != 0)
5f2047ec
JAK
2226 return _error->Errno("setegid", "Failed to setegid");
2227
fc1a78d8
MV
2228 if (setgid(pw->pw_gid) != 0)
2229 return _error->Errno("setgid", "Failed to setgid");
5f2047ec 2230
fc1a78d8
MV
2231 if (setuid(pw->pw_uid) != 0)
2232 return _error->Errno("setuid", "Failed to setuid");
f1e3c8f0 2233
3927c6da
MV
2234 // the seteuid() is probably uneeded (at least thats what the linux
2235 // man-page says about setuid(2)) but we cargo culted it anyway
5f2047ec
JAK
2236 if (seteuid(pw->pw_uid) != 0)
2237 return _error->Errno("seteuid", "Failed to seteuid");
2238
f1e3c8f0 2239 // Verify that the user has only a single group, and the correct one
5f2047ec
JAK
2240 gid_t groups[1];
2241 if (getgroups(1, groups) != 1)
2242 return _error->Errno("getgroups", "Could not get new groups");
2243 if (groups[0] != pw->pw_gid)
2244 return _error->Error("Could not switch group");
f1e3c8f0
JAK
2245
2246 // Verify that gid, egid, uid, and euid changed
5f2047ec
JAK
2247 if (getgid() != pw->pw_gid)
2248 return _error->Error("Could not switch group");
2249 if (getegid() != pw->pw_gid)
2250 return _error->Error("Could not switch effective group");
2251 if (getuid() != pw->pw_uid)
2252 return _error->Error("Could not switch user");
2253 if (geteuid() != pw->pw_uid)
2254 return _error->Error("Could not switch effective user");
2255
550ab420 2256#ifdef HAVE_GETRESUID
48ed0977 2257 // verify that the saved set-user-id was changed as well
550ab420
JAK
2258 uid_t ruid = 0;
2259 uid_t euid = 0;
2260 uid_t suid = 0;
2261 if (getresuid(&ruid, &euid, &suid))
2262 return _error->Errno("getresuid", "Could not get saved set-user-ID");
2263 if (suid != pw->pw_uid)
2264 return _error->Error("Could not switch saved set-user-ID");
2265#endif
2266
2267#ifdef HAVE_GETRESGID
48ed0977 2268 // verify that the saved set-group-id was changed as well
550ab420
JAK
2269 gid_t rgid = 0;
2270 gid_t egid = 0;
2271 gid_t sgid = 0;
2272 if (getresgid(&rgid, &egid, &sgid))
2273 return _error->Errno("getresuid", "Could not get saved set-group-ID");
2274 if (sgid != pw->pw_gid)
2275 return _error->Error("Could not switch saved set-group-ID");
2276#endif
2277
bdc00df5
JAK
2278 // Check that uid and gid changes do not work anymore
2279 if (pw->pw_gid != old_gid && (setgid(old_gid) != -1 || setegid(old_gid) != -1))
2280 return _error->Error("Could restore a gid to root, privilege dropping did not work");
2281
2282 if (pw->pw_uid != old_uid && (setuid(old_uid) != -1 || seteuid(old_uid) != -1))
2283 return _error->Error("Could restore a uid to root, privilege dropping did not work");
2284
fc1a78d8
MV
2285 return true;
2286}
f8aba23f 2287 /*}}}*/