]> git.saurik.com Git - apt.git/blame - apt-pkg/contrib/fileutl.cc
implement generic config fallback for methods
[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>
98cc7fd2 55#include <memory>
2cae0ccb 56
7efb8c8e
DK
57#ifdef HAVE_ZLIB
58 #include <zlib.h>
699b209e 59#endif
c4997486
DK
60#ifdef HAVE_BZ2
61 #include <bzlib.h>
62#endif
7f350a37
DK
63#ifdef HAVE_LZMA
64 #include <lzma.h>
2cae0ccb 65#endif
e3fbd54c
JAK
66#ifdef HAVE_LZ4
67 #include <lz4frame.h>
68#endif
05eab8af
AC
69#include <endian.h>
70#include <stdint.h>
ea542140 71
3927c6da
MV
72#if __gnu_linux__
73#include <sys/prctl.h>
74#endif
75
ea542140 76#include <apti18n.h>
578bfd0a
AL
77 /*}}}*/
78
4d055c05
AL
79using namespace std;
80
40940e63
JAK
81/* Should be a multiple of the common page size (4096) */
82static constexpr unsigned long long APT_BUFFER_SIZE = 64 * 1024;
83
614adaa0
MV
84// RunScripts - Run a set of scripts from a configuration subtree /*{{{*/
85// ---------------------------------------------------------------------
86/* */
87bool RunScripts(const char *Cnf)
88{
89 Configuration::Item const *Opts = _config->Tree(Cnf);
90 if (Opts == 0 || Opts->Child == 0)
91 return true;
92 Opts = Opts->Child;
93
94 // Fork for running the system calls
95 pid_t Child = ExecFork();
96
97 // This is the child
98 if (Child == 0)
99 {
cfba4f69
MV
100 if (_config->FindDir("DPkg::Chroot-Directory","/") != "/")
101 {
102 std::cerr << "Chrooting into "
103 << _config->FindDir("DPkg::Chroot-Directory")
104 << std::endl;
105 if (chroot(_config->FindDir("DPkg::Chroot-Directory","/").c_str()) != 0)
106 _exit(100);
107 }
108
614adaa0
MV
109 if (chdir("/tmp/") != 0)
110 _exit(100);
111
112 unsigned int Count = 1;
113 for (; Opts != 0; Opts = Opts->Next, Count++)
114 {
115 if (Opts->Value.empty() == true)
116 continue;
e5b7e019
MV
117
118 if(_config->FindB("Debug::RunScripts", false) == true)
119 std::clog << "Running external script: '"
120 << Opts->Value << "'" << std::endl;
121
614adaa0
MV
122 if (system(Opts->Value.c_str()) != 0)
123 _exit(100+Count);
124 }
125 _exit(0);
126 }
127
128 // Wait for the child
129 int Status = 0;
130 while (waitpid(Child,&Status,0) != Child)
131 {
132 if (errno == EINTR)
133 continue;
134 return _error->Errno("waitpid","Couldn't wait for subprocess");
135 }
136
137 // Restore sig int/quit
138 signal(SIGQUIT,SIG_DFL);
139 signal(SIGINT,SIG_DFL);
140
141 // Check for an error code.
142 if (WIFEXITED(Status) == 0 || WEXITSTATUS(Status) != 0)
143 {
144 unsigned int Count = WEXITSTATUS(Status);
145 if (Count > 100)
146 {
147 Count -= 100;
148 for (; Opts != 0 && Count != 1; Opts = Opts->Next, Count--);
149 _error->Error("Problem executing scripts %s '%s'",Cnf,Opts->Value.c_str());
150 }
151
152 return _error->Error("Sub-process returned an error code");
153 }
154
155 return true;
156}
157 /*}}}*/
158
578bfd0a
AL
159// CopyFile - Buffered copy of a file /*{{{*/
160// ---------------------------------------------------------------------
161/* The caller is expected to set things so that failure causes erasure */
8b89e57f 162bool CopyFile(FileFd &From,FileFd &To)
578bfd0a 163{
2128d3fc
DK
164 if (From.IsOpen() == false || To.IsOpen() == false ||
165 From.Failed() == true || To.Failed() == true)
578bfd0a 166 return false;
e977b8b9 167
578bfd0a 168 // Buffered copy between fds
40940e63 169 constexpr size_t BufSize = APT_BUFFER_SIZE;
0c93e388 170 std::unique_ptr<unsigned char[]> Buf(new unsigned char[BufSize]);
e977b8b9
DK
171 unsigned long long ToRead = 0;
172 do {
173 if (From.Read(Buf.get(),BufSize, &ToRead) == false ||
5df91bc7 174 To.Write(Buf.get(),ToRead) == false)
578bfd0a 175 return false;
e977b8b9 176 } while (ToRead != 0);
578bfd0a 177
ce1f3a2c
DK
178 return true;
179}
180 /*}}}*/
181bool RemoveFile(char const * const Function, std::string const &FileName)/*{{{*/
182{
183 if (FileName == "/dev/null")
184 return true;
185 errno = 0;
186 if (unlink(FileName.c_str()) != 0)
187 {
188 if (errno == ENOENT)
189 return true;
190
191 return _error->WarningE(Function,_("Problem unlinking the file %s"), FileName.c_str());
192 }
e977b8b9 193 return true;
578bfd0a
AL
194}
195 /*}}}*/
196// GetLock - Gets a lock file /*{{{*/
197// ---------------------------------------------------------------------
198/* This will create an empty file of the given name and lock it. Once this
199 is done all other calls to GetLock in any other process will fail with
200 -1. The return result is the fd of the file, the call should call
201 close at some time. */
202int GetLock(string File,bool Errors)
203{
f659b39a
OS
204 // GetLock() is used in aptitude on directories with public-write access
205 // Use O_NOFOLLOW here to prevent symlink traversal attacks
206 int FD = open(File.c_str(),O_RDWR | O_CREAT | O_NOFOLLOW,0640);
578bfd0a
AL
207 if (FD < 0)
208 {
1e3f4083 209 // Read only .. can't have locking problems there.
b2e465d6
AL
210 if (errno == EROFS)
211 {
212 _error->Warning(_("Not using locking for read only lock file %s"),File.c_str());
213 return dup(0); // Need something for the caller to close
214 }
215
578bfd0a 216 if (Errors == true)
b2e465d6
AL
217 _error->Errno("open",_("Could not open lock file %s"),File.c_str());
218
219 // Feh.. We do this to distinguish the lock vs open case..
220 errno = EPERM;
578bfd0a
AL
221 return -1;
222 }
b2e465d6
AL
223 SetCloseExec(FD,true);
224
1e3f4083 225 // Acquire a write lock
578bfd0a 226 struct flock fl;
c71bc556
AL
227 fl.l_type = F_WRLCK;
228 fl.l_whence = SEEK_SET;
229 fl.l_start = 0;
230 fl.l_len = 0;
578bfd0a
AL
231 if (fcntl(FD,F_SETLK,&fl) == -1)
232 {
3d165906
MV
233 // always close to not leak resources
234 int Tmp = errno;
235 close(FD);
236 errno = Tmp;
237
d89df07a
AL
238 if (errno == ENOLCK)
239 {
b2e465d6
AL
240 _error->Warning(_("Not using locking for nfs mounted lock file %s"),File.c_str());
241 return dup(0); // Need something for the caller to close
3d165906
MV
242 }
243
578bfd0a 244 if (Errors == true)
b2e465d6
AL
245 _error->Errno("open",_("Could not get lock %s"),File.c_str());
246
578bfd0a
AL
247 return -1;
248 }
249
250 return FD;
251}
252 /*}}}*/
253// FileExists - Check if a file exists /*{{{*/
254// ---------------------------------------------------------------------
36f1098a 255/* Beware: Directories are also files! */
578bfd0a
AL
256bool FileExists(string File)
257{
258 struct stat Buf;
259 if (stat(File.c_str(),&Buf) != 0)
260 return false;
261 return true;
262}
263 /*}}}*/
36f1098a
DK
264// RealFileExists - Check if a file exists and if it is really a file /*{{{*/
265// ---------------------------------------------------------------------
266/* */
267bool RealFileExists(string File)
268{
269 struct stat Buf;
270 if (stat(File.c_str(),&Buf) != 0)
271 return false;
272 return ((Buf.st_mode & S_IFREG) != 0);
273}
274 /*}}}*/
1cd1c398
DK
275// DirectoryExists - Check if a directory exists and is really one /*{{{*/
276// ---------------------------------------------------------------------
277/* */
278bool DirectoryExists(string const &Path)
279{
280 struct stat Buf;
281 if (stat(Path.c_str(),&Buf) != 0)
282 return false;
283 return ((Buf.st_mode & S_IFDIR) != 0);
284}
285 /*}}}*/
286// CreateDirectory - poor man's mkdir -p guarded by a parent directory /*{{{*/
287// ---------------------------------------------------------------------
288/* This method will create all directories needed for path in good old
289 mkdir -p style but refuses to do this if Parent is not a prefix of
290 this Path. Example: /var/cache/ and /var/cache/apt/archives are given,
291 so it will create apt/archives if /var/cache exists - on the other
292 hand if the parent is /var/lib the creation will fail as this path
293 is not a parent of the path to be generated. */
294bool CreateDirectory(string const &Parent, string const &Path)
295{
296 if (Parent.empty() == true || Path.empty() == true)
297 return false;
298
299 if (DirectoryExists(Path) == true)
300 return true;
301
302 if (DirectoryExists(Parent) == false)
303 return false;
304
305 // we are not going to create directories "into the blue"
9ce3cfc9 306 if (Path.compare(0, Parent.length(), Parent) != 0)
1cd1c398
DK
307 return false;
308
309 vector<string> const dirs = VectorizeString(Path.substr(Parent.size()), '/');
310 string progress = Parent;
311 for (vector<string>::const_iterator d = dirs.begin(); d != dirs.end(); ++d)
312 {
313 if (d->empty() == true)
314 continue;
315
316 progress.append("/").append(*d);
317 if (DirectoryExists(progress) == true)
318 continue;
319
320 if (mkdir(progress.c_str(), 0755) != 0)
321 return false;
322 }
323 return true;
324}
325 /*}}}*/
7753e468 326// CreateAPTDirectoryIfNeeded - ensure that the given directory exists /*{{{*/
b29c3712
DK
327// ---------------------------------------------------------------------
328/* a small wrapper around CreateDirectory to check if it exists and to
329 remove the trailing "/apt/" from the parent directory if needed */
7753e468 330bool CreateAPTDirectoryIfNeeded(string const &Parent, string const &Path)
b29c3712
DK
331{
332 if (DirectoryExists(Path) == true)
333 return true;
334
335 size_t const len = Parent.size();
336 if (len > 5 && Parent.find("/apt/", len - 6, 5) == len - 5)
337 {
338 if (CreateDirectory(Parent.substr(0,len-5), Path) == true)
339 return true;
340 }
341 else if (CreateDirectory(Parent, Path) == true)
342 return true;
343
344 return false;
345}
346 /*}}}*/
46e39c8e
MV
347// GetListOfFilesInDir - returns a vector of files in the given dir /*{{{*/
348// ---------------------------------------------------------------------
349/* If an extension is given only files with this extension are included
350 in the returned vector, otherwise every "normal" file is included. */
b39c1859
MV
351std::vector<string> GetListOfFilesInDir(string const &Dir, string const &Ext,
352 bool const &SortList, bool const &AllowNoExt)
353{
354 std::vector<string> ext;
355 ext.reserve(2);
356 if (Ext.empty() == false)
357 ext.push_back(Ext);
358 if (AllowNoExt == true && ext.empty() == false)
359 ext.push_back("");
360 return GetListOfFilesInDir(Dir, ext, SortList);
361}
362std::vector<string> GetListOfFilesInDir(string const &Dir, std::vector<string> const &Ext,
363 bool const &SortList)
364{
365 // Attention debuggers: need to be set with the environment config file!
366 bool const Debug = _config->FindB("Debug::GetListOfFilesInDir", false);
367 if (Debug == true)
368 {
369 std::clog << "Accept in " << Dir << " only files with the following " << Ext.size() << " extensions:" << std::endl;
370 if (Ext.empty() == true)
371 std::clog << "\tNO extension" << std::endl;
372 else
373 for (std::vector<string>::const_iterator e = Ext.begin();
374 e != Ext.end(); ++e)
375 std::clog << '\t' << (e->empty() == true ? "NO" : *e) << " extension" << std::endl;
376 }
377
46e39c8e 378 std::vector<string> List;
36f1098a 379
69c2ecbd 380 if (DirectoryExists(Dir) == false)
36f1098a
DK
381 {
382 _error->Error(_("List of files can't be created as '%s' is not a directory"), Dir.c_str());
383 return List;
384 }
385
1408e219 386 Configuration::MatchAgainstConfig SilentIgnore("Dir::Ignore-Files-Silently");
46e39c8e
MV
387 DIR *D = opendir(Dir.c_str());
388 if (D == 0)
389 {
390 _error->Errno("opendir",_("Unable to read %s"),Dir.c_str());
391 return List;
392 }
393
394 for (struct dirent *Ent = readdir(D); Ent != 0; Ent = readdir(D))
395 {
b39c1859 396 // skip "hidden" files
46e39c8e
MV
397 if (Ent->d_name[0] == '.')
398 continue;
399
491058e3
DK
400 // Make sure it is a file and not something else
401 string const File = flCombine(Dir,Ent->d_name);
402#ifdef _DIRENT_HAVE_D_TYPE
403 if (Ent->d_type != DT_REG)
404#endif
405 {
69c2ecbd 406 if (RealFileExists(File) == false)
491058e3 407 {
84e254d6
DK
408 // do not show ignoration warnings for directories
409 if (
410#ifdef _DIRENT_HAVE_D_TYPE
411 Ent->d_type == DT_DIR ||
412#endif
69c2ecbd 413 DirectoryExists(File) == true)
84e254d6 414 continue;
491058e3
DK
415 if (SilentIgnore.Match(Ent->d_name) == false)
416 _error->Notice(_("Ignoring '%s' in directory '%s' as it is not a regular file"), Ent->d_name, Dir.c_str());
417 continue;
418 }
419 }
420
b39c1859
MV
421 // check for accepted extension:
422 // no extension given -> periods are bad as hell!
423 // extensions given -> "" extension allows no extension
424 if (Ext.empty() == false)
425 {
426 string d_ext = flExtension(Ent->d_name);
427 if (d_ext == Ent->d_name) // no extension
428 {
429 if (std::find(Ext.begin(), Ext.end(), "") == Ext.end())
430 {
431 if (Debug == true)
432 std::clog << "Bad file: " << Ent->d_name << " → no extension" << std::endl;
5edc3966 433 if (SilentIgnore.Match(Ent->d_name) == false)
491058e3 434 _error->Notice(_("Ignoring file '%s' in directory '%s' as it has no filename extension"), Ent->d_name, Dir.c_str());
b39c1859
MV
435 continue;
436 }
437 }
438 else if (std::find(Ext.begin(), Ext.end(), d_ext) == Ext.end())
439 {
440 if (Debug == true)
441 std::clog << "Bad file: " << Ent->d_name << " → bad extension »" << flExtension(Ent->d_name) << "«" << std::endl;
1408e219 442 if (SilentIgnore.Match(Ent->d_name) == false)
491058e3 443 _error->Notice(_("Ignoring file '%s' in directory '%s' as it has an invalid filename extension"), Ent->d_name, Dir.c_str());
b39c1859
MV
444 continue;
445 }
446 }
46e39c8e 447
b39c1859 448 // Skip bad filenames ala run-parts
46e39c8e
MV
449 const char *C = Ent->d_name;
450 for (; *C != 0; ++C)
451 if (isalpha(*C) == 0 && isdigit(*C) == 0
9d39208a 452 && *C != '_' && *C != '-' && *C != ':') {
b39c1859
MV
453 // no required extension -> dot is a bad character
454 if (*C == '.' && Ext.empty() == false)
455 continue;
46e39c8e 456 break;
b39c1859 457 }
46e39c8e 458
b39c1859 459 // we don't reach the end of the name -> bad character included
46e39c8e 460 if (*C != 0)
b39c1859
MV
461 {
462 if (Debug == true)
463 std::clog << "Bad file: " << Ent->d_name << " → bad character »"
464 << *C << "« in filename (period allowed: " << (Ext.empty() ? "no" : "yes") << ")" << std::endl;
465 continue;
466 }
467
fbb2c7e0
DK
468 // skip filenames which end with a period. These are never valid
469 if (*(C - 1) == '.')
470 {
471 if (Debug == true)
472 std::clog << "Bad file: " << Ent->d_name << " → Period as last character" << std::endl;
473 continue;
474 }
475
476 if (Debug == true)
477 std::clog << "Accept file: " << Ent->d_name << " in " << Dir << std::endl;
478 List.push_back(File);
479 }
480 closedir(D);
481
482 if (SortList == true)
483 std::sort(List.begin(),List.end());
484 return List;
485}
486std::vector<string> GetListOfFilesInDir(string const &Dir, bool SortList)
487{
488 bool const Debug = _config->FindB("Debug::GetListOfFilesInDir", false);
489 if (Debug == true)
490 std::clog << "Accept in " << Dir << " all regular files" << std::endl;
491
492 std::vector<string> List;
493
69c2ecbd 494 if (DirectoryExists(Dir) == false)
fbb2c7e0
DK
495 {
496 _error->Error(_("List of files can't be created as '%s' is not a directory"), Dir.c_str());
497 return List;
498 }
499
500 DIR *D = opendir(Dir.c_str());
501 if (D == 0)
502 {
503 _error->Errno("opendir",_("Unable to read %s"),Dir.c_str());
504 return List;
505 }
506
507 for (struct dirent *Ent = readdir(D); Ent != 0; Ent = readdir(D))
508 {
509 // skip "hidden" files
510 if (Ent->d_name[0] == '.')
511 continue;
512
513 // Make sure it is a file and not something else
514 string const File = flCombine(Dir,Ent->d_name);
515#ifdef _DIRENT_HAVE_D_TYPE
516 if (Ent->d_type != DT_REG)
517#endif
518 {
69c2ecbd 519 if (RealFileExists(File) == false)
fbb2c7e0
DK
520 {
521 if (Debug == true)
522 std::clog << "Bad file: " << Ent->d_name << " → it is not a real file" << std::endl;
523 continue;
524 }
525 }
526
527 // Skip bad filenames ala run-parts
528 const char *C = Ent->d_name;
529 for (; *C != 0; ++C)
530 if (isalpha(*C) == 0 && isdigit(*C) == 0
531 && *C != '_' && *C != '-' && *C != '.')
532 break;
533
534 // we don't reach the end of the name -> bad character included
535 if (*C != 0)
536 {
537 if (Debug == true)
538 std::clog << "Bad file: " << Ent->d_name << " → bad character »" << *C << "« in filename" << std::endl;
539 continue;
540 }
541
b39c1859
MV
542 // skip filenames which end with a period. These are never valid
543 if (*(C - 1) == '.')
544 {
545 if (Debug == true)
546 std::clog << "Bad file: " << Ent->d_name << " → Period as last character" << std::endl;
46e39c8e 547 continue;
b39c1859 548 }
46e39c8e 549
b39c1859
MV
550 if (Debug == true)
551 std::clog << "Accept file: " << Ent->d_name << " in " << Dir << std::endl;
46e39c8e
MV
552 List.push_back(File);
553 }
554 closedir(D);
555
556 if (SortList == true)
557 std::sort(List.begin(),List.end());
558 return List;
559}
560 /*}}}*/
578bfd0a
AL
561// SafeGetCWD - This is a safer getcwd that returns a dynamic string /*{{{*/
562// ---------------------------------------------------------------------
563/* We return / on failure. */
564string SafeGetCWD()
565{
566 // Stash the current dir.
567 char S[300];
568 S[0] = 0;
7f25bdff 569 if (getcwd(S,sizeof(S)-2) == 0)
578bfd0a 570 return "/";
7f25bdff
AL
571 unsigned int Len = strlen(S);
572 S[Len] = '/';
573 S[Len+1] = 0;
578bfd0a
AL
574 return S;
575}
576 /*}}}*/
2ec858bc
MV
577// GetModificationTime - Get the mtime of the given file or -1 on error /*{{{*/
578// ---------------------------------------------------------------------
579/* We return / on failure. */
580time_t GetModificationTime(string const &Path)
581{
582 struct stat St;
583 if (stat(Path.c_str(), &St) < 0)
584 return -1;
585 return St.st_mtime;
586}
587 /*}}}*/
8ce4327b
AL
588// flNotDir - Strip the directory from the filename /*{{{*/
589// ---------------------------------------------------------------------
590/* */
591string flNotDir(string File)
592{
593 string::size_type Res = File.rfind('/');
594 if (Res == string::npos)
595 return File;
596 Res++;
597 return string(File,Res,Res - File.length());
598}
599 /*}}}*/
d38b7b3d
AL
600// flNotFile - Strip the file from the directory name /*{{{*/
601// ---------------------------------------------------------------------
171c45bc 602/* Result ends in a / */
d38b7b3d
AL
603string flNotFile(string File)
604{
605 string::size_type Res = File.rfind('/');
606 if (Res == string::npos)
171c45bc 607 return "./";
d38b7b3d
AL
608 Res++;
609 return string(File,0,Res);
610}
611 /*}}}*/
b2e465d6
AL
612// flExtension - Return the extension for the file /*{{{*/
613// ---------------------------------------------------------------------
614/* */
615string flExtension(string File)
616{
617 string::size_type Res = File.rfind('.');
618 if (Res == string::npos)
619 return File;
620 Res++;
621 return string(File,Res,Res - File.length());
622}
623 /*}}}*/
421c8d10
AL
624// flNoLink - If file is a symlink then deref it /*{{{*/
625// ---------------------------------------------------------------------
626/* If the name is not a link then the returned path is the input. */
627string flNoLink(string File)
628{
629 struct stat St;
630 if (lstat(File.c_str(),&St) != 0 || S_ISLNK(St.st_mode) == 0)
631 return File;
632 if (stat(File.c_str(),&St) != 0)
633 return File;
634
635 /* Loop resolving the link. There is no need to limit the number of
636 loops because the stat call above ensures that the symlink is not
637 circular */
638 char Buffer[1024];
639 string NFile = File;
640 while (1)
641 {
642 // Read the link
3286ad13 643 ssize_t Res;
421c8d10 644 if ((Res = readlink(NFile.c_str(),Buffer,sizeof(Buffer))) <= 0 ||
3286ad13 645 (size_t)Res >= sizeof(Buffer))
421c8d10
AL
646 return File;
647
648 // Append or replace the previous path
649 Buffer[Res] = 0;
650 if (Buffer[0] == '/')
651 NFile = Buffer;
652 else
653 NFile = flNotFile(NFile) + Buffer;
654
655 // See if we are done
656 if (lstat(NFile.c_str(),&St) != 0)
657 return File;
658 if (S_ISLNK(St.st_mode) == 0)
659 return NFile;
660 }
661}
662 /*}}}*/
b2e465d6
AL
663// flCombine - Combine a file and a directory /*{{{*/
664// ---------------------------------------------------------------------
665/* If the file is an absolute path then it is just returned, otherwise
666 the directory is pre-pended to it. */
667string flCombine(string Dir,string File)
668{
669 if (File.empty() == true)
670 return string();
671
672 if (File[0] == '/' || Dir.empty() == true)
673 return File;
674 if (File.length() >= 2 && File[0] == '.' && File[1] == '/')
675 return File;
676 if (Dir[Dir.length()-1] == '/')
677 return Dir + File;
678 return Dir + '/' + File;
679}
680 /*}}}*/
53ac87ac
MV
681// flAbsPath - Return the absolute path of the filename /*{{{*/
682// ---------------------------------------------------------------------
683/* */
684string flAbsPath(string File)
685{
686 char *p = realpath(File.c_str(), NULL);
687 if (p == NULL)
688 {
95278287 689 _error->Errno("realpath", "flAbsPath on %s failed", File.c_str());
53ac87ac
MV
690 return "";
691 }
692 std::string AbsPath(p);
693 free(p);
694 return AbsPath;
695}
696 /*}}}*/
bbd8308c
DK
697std::string flNormalize(std::string file) /*{{{*/
698{
699 if (file.empty())
700 return file;
701 // do some normalisation by removing // and /./ from the path
702 size_t found = string::npos;
703 while ((found = file.find("/./")) != string::npos)
704 file.replace(found, 3, "/");
705 while ((found = file.find("//")) != string::npos)
706 file.replace(found, 2, "/");
707
708 if (APT::String::Startswith(file, "/dev/null"))
709 {
710 file.erase(strlen("/dev/null"));
711 return file;
712 }
713 return file;
714}
715 /*}}}*/
3b5421b4
AL
716// SetCloseExec - Set the close on exec flag /*{{{*/
717// ---------------------------------------------------------------------
718/* */
719void SetCloseExec(int Fd,bool Close)
720{
721 if (fcntl(Fd,F_SETFD,(Close == false)?0:FD_CLOEXEC) != 0)
722 {
723 cerr << "FATAL -> Could not set close on exec " << strerror(errno) << endl;
724 exit(100);
725 }
726}
727 /*}}}*/
728// SetNonBlock - Set the nonblocking flag /*{{{*/
729// ---------------------------------------------------------------------
730/* */
731void SetNonBlock(int Fd,bool Block)
732{
0a8a80e5
AL
733 int Flags = fcntl(Fd,F_GETFL) & (~O_NONBLOCK);
734 if (fcntl(Fd,F_SETFL,Flags | ((Block == false)?0:O_NONBLOCK)) != 0)
3b5421b4
AL
735 {
736 cerr << "FATAL -> Could not set non-blocking flag " << strerror(errno) << endl;
737 exit(100);
738 }
739}
740 /*}}}*/
741// WaitFd - Wait for a FD to become readable /*{{{*/
742// ---------------------------------------------------------------------
b2e465d6 743/* This waits for a FD to become readable using select. It is useful for
6d5dd02a
AL
744 applications making use of non-blocking sockets. The timeout is
745 in seconds. */
1084d58a 746bool WaitFd(int Fd,bool write,unsigned long timeout)
3b5421b4
AL
747{
748 fd_set Set;
cc2313b7 749 struct timeval tv;
3b5421b4
AL
750 FD_ZERO(&Set);
751 FD_SET(Fd,&Set);
6d5dd02a
AL
752 tv.tv_sec = timeout;
753 tv.tv_usec = 0;
1084d58a 754 if (write == true)
b0db36b1
AL
755 {
756 int Res;
757 do
758 {
759 Res = select(Fd+1,0,&Set,0,(timeout != 0?&tv:0));
760 }
761 while (Res < 0 && errno == EINTR);
762
763 if (Res <= 0)
764 return false;
1084d58a
AL
765 }
766 else
767 {
b0db36b1
AL
768 int Res;
769 do
770 {
771 Res = select(Fd+1,&Set,0,0,(timeout != 0?&tv:0));
772 }
773 while (Res < 0 && errno == EINTR);
774
775 if (Res <= 0)
776 return false;
cc2313b7 777 }
1084d58a 778
3b5421b4
AL
779 return true;
780}
781 /*}}}*/
96ae6de5 782// MergeKeepFdsFromConfiguration - Merge APT::Keep-Fds configuration /*{{{*/
54676e1a 783// ---------------------------------------------------------------------
96ae6de5
MV
784/* This is used to merge the APT::Keep-Fds with the provided KeepFDs
785 * set.
786 */
787void MergeKeepFdsFromConfiguration(std::set<int> &KeepFDs)
e45c4617 788{
e45c4617
MV
789 Configuration::Item const *Opts = _config->Tree("APT::Keep-Fds");
790 if (Opts != 0 && Opts->Child != 0)
791 {
792 Opts = Opts->Child;
793 for (; Opts != 0; Opts = Opts->Next)
794 {
795 if (Opts->Value.empty() == true)
796 continue;
797 int fd = atoi(Opts->Value.c_str());
798 KeepFDs.insert(fd);
799 }
800 }
96ae6de5
MV
801}
802 /*}}}*/
54676e1a
AL
803// ExecFork - Magical fork that sanitizes the context before execing /*{{{*/
804// ---------------------------------------------------------------------
805/* This is used if you want to cleanse the environment for the forked
806 child, it fixes up the important signals and nukes all of the fds,
807 otherwise acts like normal fork. */
75ef8f14 808pid_t ExecFork()
96ae6de5
MV
809{
810 set<int> KeepFDs;
811 // we need to merge the Keep-Fds as external tools like
812 // debconf-apt-progress use it
813 MergeKeepFdsFromConfiguration(KeepFDs);
e45c4617
MV
814 return ExecFork(KeepFDs);
815}
816
817pid_t ExecFork(std::set<int> KeepFDs)
54676e1a
AL
818{
819 // Fork off the process
820 pid_t Process = fork();
821 if (Process < 0)
822 {
823 cerr << "FATAL -> Failed to fork." << endl;
824 exit(100);
825 }
826
827 // Spawn the subprocess
828 if (Process == 0)
829 {
830 // Setup the signals
831 signal(SIGPIPE,SIG_DFL);
832 signal(SIGQUIT,SIG_DFL);
833 signal(SIGINT,SIG_DFL);
834 signal(SIGWINCH,SIG_DFL);
835 signal(SIGCONT,SIG_DFL);
836 signal(SIGTSTP,SIG_DFL);
75ef8f14 837
be4d908f
JAK
838 DIR *dir = opendir("/proc/self/fd");
839 if (dir != NULL)
75ef8f14 840 {
be4d908f
JAK
841 struct dirent *ent;
842 while ((ent = readdir(dir)))
843 {
844 int fd = atoi(ent->d_name);
845 // If fd > 0, it was a fd number and not . or ..
846 if (fd >= 3 && KeepFDs.find(fd) == KeepFDs.end())
847 fcntl(fd,F_SETFD,FD_CLOEXEC);
848 }
849 closedir(dir);
850 } else {
851 long ScOpenMax = sysconf(_SC_OPEN_MAX);
852 // Close all of our FDs - just in case
853 for (int K = 3; K != ScOpenMax; K++)
854 {
855 if(KeepFDs.find(K) == KeepFDs.end())
856 fcntl(K,F_SETFD,FD_CLOEXEC);
857 }
75ef8f14 858 }
54676e1a
AL
859 }
860
861 return Process;
862}
863 /*}}}*/
ddc1d8d0
AL
864// ExecWait - Fancy waitpid /*{{{*/
865// ---------------------------------------------------------------------
2c9a72d1 866/* Waits for the given sub process. If Reap is set then no errors are
ddc1d8d0
AL
867 generated. Otherwise a failed subprocess will generate a proper descriptive
868 message */
3826564e 869bool ExecWait(pid_t Pid,const char *Name,bool Reap)
ddc1d8d0
AL
870{
871 if (Pid <= 1)
872 return true;
873
874 // Wait and collect the error code
875 int Status;
876 while (waitpid(Pid,&Status,0) != Pid)
877 {
878 if (errno == EINTR)
879 continue;
880
881 if (Reap == true)
882 return false;
883
db0db9fe 884 return _error->Error(_("Waited for %s but it wasn't there"),Name);
ddc1d8d0
AL
885 }
886
887
888 // Check for an error code.
889 if (WIFEXITED(Status) == 0 || WEXITSTATUS(Status) != 0)
890 {
891 if (Reap == true)
892 return false;
ab7f4d7c 893 if (WIFSIGNALED(Status) != 0)
40e7fe0e 894 {
ab7f4d7c
MV
895 if( WTERMSIG(Status) == SIGSEGV)
896 return _error->Error(_("Sub-process %s received a segmentation fault."),Name);
897 else
898 return _error->Error(_("Sub-process %s received signal %u."),Name, WTERMSIG(Status));
40e7fe0e 899 }
ddc1d8d0
AL
900
901 if (WIFEXITED(Status) != 0)
b2e465d6 902 return _error->Error(_("Sub-process %s returned an error code (%u)"),Name,WEXITSTATUS(Status));
ddc1d8d0 903
b2e465d6 904 return _error->Error(_("Sub-process %s exited unexpectedly"),Name);
ddc1d8d0
AL
905 }
906
907 return true;
908}
909 /*}}}*/
f8aba23f 910// StartsWithGPGClearTextSignature - Check if a file is Pgp/GPG clearsigned /*{{{*/
fe5804fc 911bool StartsWithGPGClearTextSignature(string const &FileName)
0854ad8b
MV
912{
913 static const char* SIGMSG = "-----BEGIN PGP SIGNED MESSAGE-----\n";
1c89c98a 914 char buffer[strlen(SIGMSG)+1];
0854ad8b
MV
915 FILE* gpg = fopen(FileName.c_str(), "r");
916 if (gpg == NULL)
917 return false;
918
919 char const * const test = fgets(buffer, sizeof(buffer), gpg);
920 fclose(gpg);
921 if (test == NULL || strcmp(buffer, SIGMSG) != 0)
922 return false;
923
924 return true;
925}
f8aba23f 926 /*}}}*/
d84da499
DK
927// ChangeOwnerAndPermissionOfFile - set file attributes to requested values /*{{{*/
928bool ChangeOwnerAndPermissionOfFile(char const * const requester, char const * const file, char const * const user, char const * const group, mode_t const mode)
929{
930 if (strcmp(file, "/dev/null") == 0)
931 return true;
932 bool Res = true;
933 if (getuid() == 0 && strlen(user) != 0 && strlen(group) != 0) // if we aren't root, we can't chown, so don't try it
934 {
935 // ensure the file is owned by root and has good permissions
936 struct passwd const * const pw = getpwnam(user);
937 struct group const * const gr = getgrnam(group);
34651385 938 if (pw != NULL && gr != NULL && lchown(file, pw->pw_uid, gr->gr_gid) != 0)
d84da499
DK
939 Res &= _error->WarningE(requester, "chown to %s:%s of file %s failed", user, group, file);
940 }
34651385
DK
941 struct stat Buf;
942 if (lstat(file, &Buf) != 0 || S_ISLNK(Buf.st_mode))
943 return Res;
d84da499
DK
944 if (chmod(file, mode) != 0)
945 Res &= _error->WarningE(requester, "chmod 0%o of file %s failed", mode, file);
946 return Res;
947}
948 /*}}}*/
0854ad8b 949
38dba8cd 950struct APT_HIDDEN simple_buffer { /*{{{*/
5bdba2ca 951 size_t buffersize_max = 0;
38dba8cd
JAK
952 unsigned long long bufferstart = 0;
953 unsigned long long bufferend = 0;
5bdba2ca
JAK
954 char *buffer = nullptr;
955
956 simple_buffer() {
957 reset(4096);
958 }
959 ~simple_buffer() {
ab16ead7 960 delete[] buffer;
5bdba2ca 961 }
38dba8cd 962
ea58d39e 963 const char *get() const { return buffer + bufferstart; }
38dba8cd 964 char *get() { return buffer + bufferstart; }
f1b9bf7a
JAK
965 const char *getend() const { return buffer + bufferend; }
966 char *getend() { return buffer + bufferend; }
ea58d39e 967 bool empty() const { return bufferend <= bufferstart; }
c368b3ab 968 bool full() const { return bufferend == buffersize_max; }
f1b9bf7a 969 unsigned long long free() const { return buffersize_max - bufferend; }
ea58d39e 970 unsigned long long size() const { return bufferend-bufferstart; }
5bdba2ca
JAK
971 void reset(size_t size)
972 {
973 if (size > buffersize_max) {
974 delete[] buffer;
975 buffersize_max = size;
976 buffer = new char[size];
977 }
978 reset();
979 }
38dba8cd
JAK
980 void reset() { bufferend = bufferstart = 0; }
981 ssize_t read(void *to, unsigned long long requested_size) APT_MUSTCHECK
982 {
983 if (size() < requested_size)
984 requested_size = size();
985 memcpy(to, buffer + bufferstart, requested_size);
986 bufferstart += requested_size;
987 if (bufferstart == bufferend)
988 bufferstart = bufferend = 0;
989 return requested_size;
990 }
c368b3ab
JAK
991 ssize_t write(const void *from, unsigned long long requested_size) APT_MUSTCHECK
992 {
47fcfff8
JAK
993 if (free() < requested_size)
994 requested_size = free();
995 memcpy(getend(), from, requested_size);
c368b3ab
JAK
996 bufferend += requested_size;
997 if (bufferstart == bufferend)
998 bufferstart = bufferend = 0;
999 return requested_size;
1000 }
38dba8cd
JAK
1001};
1002 /*}}}*/
1003
65ac6aad 1004class APT_HIDDEN FileFdPrivate { /*{{{*/
88749b5d 1005 friend class BufferedWriteFileFdPrivate;
fa89055f
DK
1006protected:
1007 FileFd * const filefd;
38dba8cd 1008 simple_buffer buffer;
fa89055f
DK
1009 int compressed_fd;
1010 pid_t compressor_pid;
1011 bool is_pipe;
1012 APT::Configuration::Compressor compressor;
1013 unsigned int openmode;
1014 unsigned long long seekpos;
1d68256d
JAK
1015public:
1016
83e22e26 1017 explicit FileFdPrivate(FileFd * const pfilefd) : filefd(pfilefd),
fa89055f
DK
1018 compressed_fd(-1), compressor_pid(-1), is_pipe(false),
1019 openmode(0), seekpos(0) {};
1d68256d
JAK
1020 virtual APT::Configuration::Compressor get_compressor() const
1021 {
1022 return compressor;
1023 }
1024 virtual void set_compressor(APT::Configuration::Compressor const &compressor)
1025 {
1026 this->compressor = compressor;
1027 }
1028 virtual unsigned int get_openmode() const
1029 {
1030 return openmode;
1031 }
1032 virtual void set_openmode(unsigned int openmode)
1033 {
1034 this->openmode = openmode;
1035 }
1036 virtual bool get_is_pipe() const
1037 {
1038 return is_pipe;
1039 }
1040 virtual void set_is_pipe(bool is_pipe)
1041 {
1042 this->is_pipe = is_pipe;
1043 }
1044 virtual unsigned long long get_seekpos() const
1045 {
1046 return seekpos;
1047 }
1048 virtual void set_seekpos(unsigned long long seekpos)
1049 {
1050 this->seekpos = seekpos;
1051 }
fa89055f
DK
1052
1053 virtual bool InternalOpen(int const iFd, unsigned int const Mode) = 0;
f63123c3 1054 ssize_t InternalRead(void * To, unsigned long long Size)
fa89055f 1055 {
83e22e26
JAK
1056 // Drain the buffer if needed.
1057 if (buffer.empty() == false)
fa89055f 1058 {
83e22e26 1059 return buffer.read(To, Size);
fa89055f 1060 }
83e22e26 1061 return InternalUnbufferedRead(To, Size);
f63123c3
DK
1062 }
1063 virtual ssize_t InternalUnbufferedRead(void * const To, unsigned long long const Size) = 0;
1064 virtual bool InternalReadError() { return filefd->FileFdErrno("read",_("Read error")); }
1065 virtual char * InternalReadLine(char * To, unsigned long long Size)
1066 {
1067 if (unlikely(Size == 0))
1068 return nullptr;
01152444 1069 // Read one byte less than buffer size to have space for trailing 0.
f63123c3 1070 --Size;
01152444 1071
f63123c3
DK
1072 char * const InitialTo = To;
1073
01152444 1074 while (Size > 0) {
83e22e26 1075 if (buffer.empty() == true)
f63123c3 1076 {
83e22e26 1077 buffer.reset();
f63123c3 1078 unsigned long long actualread = 0;
72ed5f14 1079 if (filefd->Read(buffer.getend(), buffer.free(), &actualread) == false)
f63123c3 1080 return nullptr;
83e22e26
JAK
1081 buffer.bufferend = actualread;
1082 if (buffer.size() == 0)
f63123c3
DK
1083 {
1084 if (To == InitialTo)
1085 return nullptr;
1086 break;
1087 }
1088 filefd->Flags &= ~FileFd::HitEof;
1089 }
1090
83e22e26 1091 unsigned long long const OutputSize = std::min(Size, buffer.size());
b3db9d81 1092 char const * const newline = static_cast<char const * const>(memchr(buffer.get(), '\n', OutputSize));
a9024b1b
JAK
1093 // Read until end of line or up to Size bytes from the buffer.
1094 unsigned long long actualread = buffer.read(To,
1095 (newline != nullptr)
1096 ? (newline - buffer.get()) + 1
1097 : OutputSize);
1098 To += actualread;
1099 Size -= actualread;
f63123c3 1100 if (newline != nullptr)
f63123c3 1101 break;
01152444 1102 }
f63123c3
DK
1103 *To = '\0';
1104 return InitialTo;
fa89055f 1105 }
766761fd
JAK
1106 virtual bool InternalFlush()
1107 {
1108 return true;
1109 }
fa89055f
DK
1110 virtual ssize_t InternalWrite(void const * const From, unsigned long long const Size) = 0;
1111 virtual bool InternalWriteError() { return filefd->FileFdErrno("write",_("Write error")); }
1112 virtual bool InternalSeek(unsigned long long const To)
1113 {
1114 // Our poor man seeking is costly, so try to avoid it
1115 unsigned long long const iseekpos = filefd->Tell();
1116 if (iseekpos == To)
1117 return true;
1118 else if (iseekpos < To)
1119 return filefd->Skip(To - iseekpos);
1120
1121 if ((openmode & FileFd::ReadOnly) != FileFd::ReadOnly)
1122 return filefd->FileFdError("Reopen is only implemented for read-only files!");
1123 InternalClose(filefd->FileName);
1124 if (filefd->iFd != -1)
1125 close(filefd->iFd);
1126 filefd->iFd = -1;
1127 if (filefd->TemporaryFileName.empty() == false)
1128 filefd->iFd = open(filefd->TemporaryFileName.c_str(), O_RDONLY);
1129 else if (filefd->FileName.empty() == false)
1130 filefd->iFd = open(filefd->FileName.c_str(), O_RDONLY);
1131 else
1132 {
1133 if (compressed_fd > 0)
1134 if (lseek(compressed_fd, 0, SEEK_SET) != 0)
1135 filefd->iFd = compressed_fd;
1136 if (filefd->iFd < 0)
1137 return filefd->FileFdError("Reopen is not implemented for pipes opened with FileFd::OpenDescriptor()!");
1138 }
1139
1140 if (filefd->OpenInternDescriptor(openmode, compressor) == false)
1141 return filefd->FileFdError("Seek on file %s because it couldn't be reopened", filefd->FileName.c_str());
1142
83e22e26 1143 buffer.reset();
eda0c1ba 1144 set_seekpos(0);
fa89055f
DK
1145 if (To != 0)
1146 return filefd->Skip(To);
1147
1148 seekpos = To;
1149 return true;
1150 }
1151 virtual bool InternalSkip(unsigned long long Over)
1152 {
1153 unsigned long long constexpr buffersize = 1024;
1154 char buffer[buffersize];
1155 while (Over != 0)
1156 {
1157 unsigned long long toread = std::min(buffersize, Over);
1158 if (filefd->Read(buffer, toread) == false)
1159 return filefd->FileFdError("Unable to seek ahead %llu",Over);
1160 Over -= toread;
1161 }
1162 return true;
1163 }
1164 virtual bool InternalTruncate(unsigned long long const)
1165 {
1166 return filefd->FileFdError("Truncating compressed files is not implemented (%s)", filefd->FileName.c_str());
1167 }
1168 virtual unsigned long long InternalTell()
1169 {
1170 // In theory, we could just return seekpos here always instead of
1171 // seeking around, but not all users of FileFd use always Seek() and co
1172 // so d->seekpos isn't always true and we can just use it as a hint if
1173 // we have nothing else, but not always as an authority…
83e22e26 1174 return seekpos - buffer.size();
fa89055f
DK
1175 }
1176 virtual unsigned long long InternalSize()
1177 {
1178 unsigned long long size = 0;
1179 unsigned long long const oldSeek = filefd->Tell();
1180 unsigned long long constexpr ignoresize = 1024;
1181 char ignore[ignoresize];
1182 unsigned long long read = 0;
1183 do {
1184 if (filefd->Read(ignore, ignoresize, &read) == false)
1185 {
1186 filefd->Seek(oldSeek);
1187 return 0;
1188 }
1189 } while(read != 0);
1190 size = filefd->Tell();
1191 filefd->Seek(oldSeek);
1192 return size;
1193 }
1194 virtual bool InternalClose(std::string const &FileName) = 0;
1195 virtual bool InternalStream() const { return false; }
1196 virtual bool InternalAlwaysAutoClose() const { return true; }
1197
1198 virtual ~FileFdPrivate() {}
1199};
1200 /*}}}*/
cabfb880 1201class APT_HIDDEN BufferedWriteFileFdPrivate : public FileFdPrivate { /*{{{*/
88749b5d
JAK
1202protected:
1203 FileFdPrivate *wrapped;
1204 simple_buffer writebuffer;
1205
1206public:
1207
1208 explicit BufferedWriteFileFdPrivate(FileFdPrivate *Priv) :
1209 FileFdPrivate(Priv->filefd), wrapped(Priv) {};
1210
cabfb880 1211 virtual APT::Configuration::Compressor get_compressor() const APT_OVERRIDE
88749b5d
JAK
1212 {
1213 return wrapped->get_compressor();
1214 }
cabfb880 1215 virtual void set_compressor(APT::Configuration::Compressor const &compressor) APT_OVERRIDE
88749b5d
JAK
1216 {
1217 return wrapped->set_compressor(compressor);
1218 }
cabfb880 1219 virtual unsigned int get_openmode() const APT_OVERRIDE
88749b5d
JAK
1220 {
1221 return wrapped->get_openmode();
1222 }
cabfb880 1223 virtual void set_openmode(unsigned int openmode) APT_OVERRIDE
88749b5d
JAK
1224 {
1225 return wrapped->set_openmode(openmode);
1226 }
cabfb880 1227 virtual bool get_is_pipe() const APT_OVERRIDE
88749b5d
JAK
1228 {
1229 return wrapped->get_is_pipe();
1230 }
cabfb880 1231 virtual void set_is_pipe(bool is_pipe) APT_OVERRIDE
88749b5d
JAK
1232 {
1233 FileFdPrivate::set_is_pipe(is_pipe);
1234 wrapped->set_is_pipe(is_pipe);
1235 }
cabfb880 1236 virtual unsigned long long get_seekpos() const APT_OVERRIDE
88749b5d
JAK
1237 {
1238 return wrapped->get_seekpos();
1239 }
cabfb880 1240 virtual void set_seekpos(unsigned long long seekpos) APT_OVERRIDE
88749b5d
JAK
1241 {
1242 return wrapped->set_seekpos(seekpos);
1243 }
cabfb880 1244 virtual bool InternalOpen(int const iFd, unsigned int const Mode) APT_OVERRIDE
88749b5d
JAK
1245 {
1246 if (InternalFlush() == false)
1247 return false;
1248 return wrapped->InternalOpen(iFd, Mode);
1249 }
cabfb880 1250 virtual ssize_t InternalUnbufferedRead(void * const To, unsigned long long const Size) APT_OVERRIDE
88749b5d
JAK
1251 {
1252 if (InternalFlush() == false)
1253 return -1;
1254 return wrapped->InternalUnbufferedRead(To, Size);
1255
1256 }
cabfb880 1257 virtual bool InternalReadError() APT_OVERRIDE
88749b5d
JAK
1258 {
1259 return wrapped->InternalReadError();
1260 }
cabfb880 1261 virtual char * InternalReadLine(char * To, unsigned long long Size) APT_OVERRIDE
88749b5d
JAK
1262 {
1263 if (InternalFlush() == false)
1264 return nullptr;
1265 return wrapped->InternalReadLine(To, Size);
1266 }
cabfb880 1267 virtual bool InternalFlush() APT_OVERRIDE
88749b5d 1268 {
1f5062f6
JAK
1269 while (writebuffer.empty() == false) {
1270 auto written = wrapped->InternalWrite(writebuffer.get(),
1271 writebuffer.size());
1272 // Ignore interrupted syscalls
1273 if (written < 0 && errno == EINTR)
1274 continue;
1275 if (written < 0)
1996a6a7 1276 return wrapped->InternalWriteError();
88749b5d 1277
1f5062f6 1278 writebuffer.bufferstart += written;
88749b5d 1279 }
88749b5d 1280 writebuffer.reset();
8ca481e8 1281 return wrapped->InternalFlush();
88749b5d 1282 }
cabfb880 1283 virtual ssize_t InternalWrite(void const * const From, unsigned long long const Size) APT_OVERRIDE
88749b5d 1284 {
f1828e6b
JAK
1285 // Optimisation: If the buffer is empty and we have more to write than
1286 // would fit in the buffer (or equal number of bytes), write directly.
1287 if (writebuffer.empty() == true && Size >= writebuffer.free())
1288 return wrapped->InternalWrite(From, Size);
1289
1290 // Write as much into the buffer as possible and then flush if needed
070ed1c9 1291 auto written = writebuffer.write(From, Size);
88749b5d 1292
070ed1c9
JAK
1293 if (writebuffer.full() && InternalFlush() == false)
1294 return -1;
88749b5d
JAK
1295
1296 return written;
1297 }
cabfb880 1298 virtual bool InternalWriteError() APT_OVERRIDE
88749b5d
JAK
1299 {
1300 return wrapped->InternalWriteError();
1301 }
cabfb880 1302 virtual bool InternalSeek(unsigned long long const To) APT_OVERRIDE
88749b5d
JAK
1303 {
1304 if (InternalFlush() == false)
1305 return false;
1306 return wrapped->InternalSeek(To);
1307 }
cabfb880 1308 virtual bool InternalSkip(unsigned long long Over) APT_OVERRIDE
88749b5d
JAK
1309 {
1310 if (InternalFlush() == false)
1311 return false;
1312 return wrapped->InternalSkip(Over);
1313 }
cabfb880 1314 virtual bool InternalTruncate(unsigned long long const Size) APT_OVERRIDE
88749b5d
JAK
1315 {
1316 if (InternalFlush() == false)
1317 return false;
1318 return wrapped->InternalTruncate(Size);
1319 }
cabfb880 1320 virtual unsigned long long InternalTell() APT_OVERRIDE
88749b5d
JAK
1321 {
1322 if (InternalFlush() == false)
1323 return -1;
1324 return wrapped->InternalTell();
1325 }
cabfb880 1326 virtual unsigned long long InternalSize() APT_OVERRIDE
88749b5d
JAK
1327 {
1328 if (InternalFlush() == false)
1329 return -1;
1330 return wrapped->InternalSize();
1331 }
cabfb880 1332 virtual bool InternalClose(std::string const &FileName) APT_OVERRIDE
88749b5d
JAK
1333 {
1334 return wrapped->InternalClose(FileName);
1335 }
cabfb880 1336 virtual bool InternalAlwaysAutoClose() const APT_OVERRIDE
88749b5d
JAK
1337 {
1338 return wrapped->InternalAlwaysAutoClose();
1339 }
1340 virtual ~BufferedWriteFileFdPrivate()
1341 {
1342 delete wrapped;
1343 }
1344};
1345 /*}}}*/
65ac6aad 1346class APT_HIDDEN GzipFileFdPrivate: public FileFdPrivate { /*{{{*/
4239dbca 1347#ifdef HAVE_ZLIB
fa89055f
DK
1348public:
1349 gzFile gz;
cabfb880 1350 virtual bool InternalOpen(int const iFd, unsigned int const Mode) APT_OVERRIDE
fa89055f
DK
1351 {
1352 if ((Mode & FileFd::ReadWrite) == FileFd::ReadWrite)
1353 gz = gzdopen(iFd, "r+");
1354 else if ((Mode & FileFd::WriteOnly) == FileFd::WriteOnly)
1355 gz = gzdopen(iFd, "w");
1356 else
1357 gz = gzdopen(iFd, "r");
1358 filefd->Flags |= FileFd::Compressed;
1359 return gz != nullptr;
1360 }
cabfb880 1361 virtual ssize_t InternalUnbufferedRead(void * const To, unsigned long long const Size) APT_OVERRIDE
fa89055f
DK
1362 {
1363 return gzread(gz, To, Size);
1364 }
cabfb880 1365 virtual bool InternalReadError() APT_OVERRIDE
fa89055f
DK
1366 {
1367 int err;
1368 char const * const errmsg = gzerror(gz, &err);
1369 if (err != Z_ERRNO)
1370 return filefd->FileFdError("gzread: %s (%d: %s)", _("Read error"), err, errmsg);
1371 return FileFdPrivate::InternalReadError();
1372 }
cabfb880 1373 virtual char * InternalReadLine(char * To, unsigned long long Size) APT_OVERRIDE
fa89055f
DK
1374 {
1375 return gzgets(gz, To, Size);
1376 }
cabfb880 1377 virtual ssize_t InternalWrite(void const * const From, unsigned long long const Size) APT_OVERRIDE
fa89055f
DK
1378 {
1379 return gzwrite(gz,From,Size);
1380 }
cabfb880 1381 virtual bool InternalWriteError() APT_OVERRIDE
fa89055f
DK
1382 {
1383 int err;
1384 char const * const errmsg = gzerror(gz, &err);
1385 if (err != Z_ERRNO)
1386 return filefd->FileFdError("gzwrite: %s (%d: %s)", _("Write error"), err, errmsg);
1387 return FileFdPrivate::InternalWriteError();
1388 }
cabfb880 1389 virtual bool InternalSeek(unsigned long long const To) APT_OVERRIDE
fa89055f
DK
1390 {
1391 off_t const res = gzseek(gz, To, SEEK_SET);
1392 if (res != (off_t)To)
1393 return filefd->FileFdError("Unable to seek to %llu", To);
fa89055f 1394 seekpos = To;
83e22e26 1395 buffer.reset();
fa89055f
DK
1396 return true;
1397 }
cabfb880 1398 virtual bool InternalSkip(unsigned long long Over) APT_OVERRIDE
fa89055f 1399 {
83e22e26 1400 if (Over >= buffer.size())
f63123c3 1401 {
83e22e26
JAK
1402 Over -= buffer.size();
1403 buffer.reset();
f63123c3
DK
1404 }
1405 else
1406 {
83e22e26 1407 buffer.bufferstart += Over;
f63123c3
DK
1408 return true;
1409 }
1410 if (Over == 0)
1411 return true;
fa89055f
DK
1412 off_t const res = gzseek(gz, Over, SEEK_CUR);
1413 if (res < 0)
1414 return filefd->FileFdError("Unable to seek ahead %llu",Over);
1415 seekpos = res;
1416 return true;
1417 }
cabfb880 1418 virtual unsigned long long InternalTell() APT_OVERRIDE
fa89055f 1419 {
83e22e26 1420 return gztell(gz) - buffer.size();
fa89055f 1421 }
cabfb880 1422 virtual unsigned long long InternalSize() APT_OVERRIDE
fa89055f
DK
1423 {
1424 unsigned long long filesize = FileFdPrivate::InternalSize();
1425 // only check gzsize if we are actually a gzip file, just checking for
1426 // "gz" is not sufficient as uncompressed files could be opened with
1427 // gzopen in "direct" mode as well
1428 if (filesize == 0 || gzdirect(gz))
1429 return filesize;
1430
1431 off_t const oldPos = lseek(filefd->iFd, 0, SEEK_CUR);
1432 /* unfortunately zlib.h doesn't provide a gzsize(), so we have to do
1433 * this ourselves; the original (uncompressed) file size is the last 32
1434 * bits of the file */
1435 // FIXME: Size for gz-files is limited by 32bit… no largefile support
1436 if (lseek(filefd->iFd, -4, SEEK_END) < 0)
1437 {
1438 filefd->FileFdErrno("lseek","Unable to seek to end of gzipped file");
1439 return 0;
1440 }
1441 uint32_t size = 0;
1442 if (read(filefd->iFd, &size, 4) != 4)
1443 {
1444 filefd->FileFdErrno("read","Unable to read original size of gzipped file");
1445 return 0;
1446 }
1447 size = le32toh(size);
1448
1449 if (lseek(filefd->iFd, oldPos, SEEK_SET) < 0)
1450 {
1451 filefd->FileFdErrno("lseek","Unable to seek in gzipped file");
1452 return 0;
1453 }
1454 return size;
1455 }
cabfb880 1456 virtual bool InternalClose(std::string const &FileName) APT_OVERRIDE
fa89055f
DK
1457 {
1458 if (gz == nullptr)
1459 return true;
1460 int const e = gzclose(gz);
1461 gz = nullptr;
1462 // gzdclose() on empty files always fails with "buffer error" here, ignore that
1463 if (e != 0 && e != Z_BUF_ERROR)
1464 return _error->Errno("close",_("Problem closing the gzip file %s"), FileName.c_str());
1465 return true;
1466 }
1467
11755147 1468 explicit GzipFileFdPrivate(FileFd * const filefd) : FileFdPrivate(filefd), gz(nullptr) {}
fa89055f 1469 virtual ~GzipFileFdPrivate() { InternalClose(""); }
4239dbca 1470#endif
fa89055f
DK
1471};
1472 /*}}}*/
65ac6aad 1473class APT_HIDDEN Bz2FileFdPrivate: public FileFdPrivate { /*{{{*/
4239dbca 1474#ifdef HAVE_BZ2
fa89055f
DK
1475 BZFILE* bz2;
1476public:
cabfb880 1477 virtual bool InternalOpen(int const iFd, unsigned int const Mode) APT_OVERRIDE
fa89055f
DK
1478 {
1479 if ((Mode & FileFd::ReadWrite) == FileFd::ReadWrite)
1480 bz2 = BZ2_bzdopen(iFd, "r+");
1481 else if ((Mode & FileFd::WriteOnly) == FileFd::WriteOnly)
1482 bz2 = BZ2_bzdopen(iFd, "w");
1483 else
1484 bz2 = BZ2_bzdopen(iFd, "r");
1485 filefd->Flags |= FileFd::Compressed;
1486 return bz2 != nullptr;
1487 }
cabfb880 1488 virtual ssize_t InternalUnbufferedRead(void * const To, unsigned long long const Size) APT_OVERRIDE
fa89055f
DK
1489 {
1490 return BZ2_bzread(bz2, To, Size);
1491 }
cabfb880 1492 virtual bool InternalReadError() APT_OVERRIDE
fa89055f
DK
1493 {
1494 int err;
1495 char const * const errmsg = BZ2_bzerror(bz2, &err);
1496 if (err != BZ_IO_ERROR)
1497 return filefd->FileFdError("BZ2_bzread: %s %s (%d: %s)", filefd->FileName.c_str(), _("Read error"), err, errmsg);
1498 return FileFdPrivate::InternalReadError();
1499 }
cabfb880 1500 virtual ssize_t InternalWrite(void const * const From, unsigned long long const Size) APT_OVERRIDE
fa89055f
DK
1501 {
1502 return BZ2_bzwrite(bz2, (void*)From, Size);
1503 }
cabfb880 1504 virtual bool InternalWriteError() APT_OVERRIDE
fa89055f
DK
1505 {
1506 int err;
1507 char const * const errmsg = BZ2_bzerror(bz2, &err);
1508 if (err != BZ_IO_ERROR)
1509 return filefd->FileFdError("BZ2_bzwrite: %s %s (%d: %s)", filefd->FileName.c_str(), _("Write error"), err, errmsg);
1510 return FileFdPrivate::InternalWriteError();
1511 }
cabfb880
DK
1512 virtual bool InternalStream() const APT_OVERRIDE { return true; }
1513 virtual bool InternalClose(std::string const &) APT_OVERRIDE
fa89055f
DK
1514 {
1515 if (bz2 == nullptr)
1516 return true;
1517 BZ2_bzclose(bz2);
1518 bz2 = nullptr;
1519 return true;
1520 }
1521
11755147 1522 explicit Bz2FileFdPrivate(FileFd * const filefd) : FileFdPrivate(filefd), bz2(nullptr) {}
fa89055f 1523 virtual ~Bz2FileFdPrivate() { InternalClose(""); }
4239dbca 1524#endif
e3fbd54c
JAK
1525};
1526 /*}}}*/
1527class APT_HIDDEN Lz4FileFdPrivate: public FileFdPrivate { /*{{{*/
e3fbd54c
JAK
1528 static constexpr unsigned long long LZ4_HEADER_SIZE = 19;
1529 static constexpr unsigned long long LZ4_FOOTER_SIZE = 4;
1530#ifdef HAVE_LZ4
1531 LZ4F_decompressionContext_t dctx;
1532 LZ4F_compressionContext_t cctx;
1533 LZ4F_errorCode_t res;
1534 FileFd backend;
1535 simple_buffer lz4_buffer;
1536 // Count of bytes that the decompressor expects to read next, or buffer size.
40940e63 1537 size_t next_to_load = APT_BUFFER_SIZE;
e3fbd54c 1538public:
cabfb880 1539 virtual bool InternalOpen(int const iFd, unsigned int const Mode) APT_OVERRIDE
e3fbd54c
JAK
1540 {
1541 if ((Mode & FileFd::ReadWrite) == FileFd::ReadWrite)
1542 return _error->Error("lz4 only supports write or read mode");
1543
1544 if ((Mode & FileFd::WriteOnly) == FileFd::WriteOnly) {
1545 res = LZ4F_createCompressionContext(&cctx, LZ4F_VERSION);
40940e63 1546 lz4_buffer.reset(LZ4F_compressBound(APT_BUFFER_SIZE, nullptr)
e3fbd54c
JAK
1547 + LZ4_HEADER_SIZE + LZ4_FOOTER_SIZE);
1548 } else {
1549 res = LZ4F_createDecompressionContext(&dctx, LZ4F_VERSION);
40940e63 1550 lz4_buffer.reset(APT_BUFFER_SIZE);
e3fbd54c
JAK
1551 }
1552
1553 filefd->Flags |= FileFd::Compressed;
1554
1555 if (LZ4F_isError(res))
1556 return false;
1557
1558 unsigned int flags = (Mode & (FileFd::WriteOnly|FileFd::ReadOnly));
6f35be91 1559 if (backend.OpenDescriptor(iFd, flags, FileFd::None, true) == false)
e3fbd54c
JAK
1560 return false;
1561
1562 // Write the file header
1563 if ((Mode & FileFd::WriteOnly) == FileFd::WriteOnly)
1564 {
1565 res = LZ4F_compressBegin(cctx, lz4_buffer.buffer, lz4_buffer.buffersize_max, nullptr);
1566 if (LZ4F_isError(res) || backend.Write(lz4_buffer.buffer, res) == false)
1567 return false;
1568 }
1569
1570 return true;
1571 }
cabfb880 1572 virtual ssize_t InternalUnbufferedRead(void * const To, unsigned long long const Size) APT_OVERRIDE
e3fbd54c
JAK
1573 {
1574 /* Keep reading as long as the compressor still wants to read */
1575 while (next_to_load) {
1576 // Fill compressed buffer;
1577 if (lz4_buffer.empty()) {
1578 unsigned long long read;
1579 /* Reset - if LZ4 decompressor wants to read more, allocate more */
1580 lz4_buffer.reset(next_to_load);
1581 if (backend.Read(lz4_buffer.getend(), lz4_buffer.free(), &read) == false)
1582 return -1;
1583 lz4_buffer.bufferend += read;
1584
1585 /* Expected EOF */
1586 if (read == 0) {
1587 res = -1;
1588 return filefd->FileFdError("LZ4F: %s %s",
1589 filefd->FileName.c_str(),
1590 _("Unexpected end of file")), -1;
1591 }
1592 }
1593 // Drain compressed buffer as far as possible.
1594 size_t in = lz4_buffer.size();
1595 size_t out = Size;
1596
1597 res = LZ4F_decompress(dctx, To, &out, lz4_buffer.get(), &in, nullptr);
1598 if (LZ4F_isError(res))
1599 return -1;
1600
1601 next_to_load = res;
1602 lz4_buffer.bufferstart += in;
1603
1604 if (out != 0)
1605 return out;
1606 }
1607
1608 return 0;
1609 }
cabfb880 1610 virtual bool InternalReadError() APT_OVERRIDE
e3fbd54c
JAK
1611 {
1612 char const * const errmsg = LZ4F_getErrorName(res);
1613
1614 return filefd->FileFdError("LZ4F: %s %s (%zu: %s)", filefd->FileName.c_str(), _("Read error"), res, errmsg);
1615 }
cabfb880 1616 virtual ssize_t InternalWrite(void const * const From, unsigned long long const Size) APT_OVERRIDE
e3fbd54c 1617 {
40940e63 1618 unsigned long long const towrite = std::min(APT_BUFFER_SIZE, Size);
e3fbd54c
JAK
1619
1620 res = LZ4F_compressUpdate(cctx,
1621 lz4_buffer.buffer, lz4_buffer.buffersize_max,
1622 From, towrite, nullptr);
1623
1624 if (LZ4F_isError(res) || backend.Write(lz4_buffer.buffer, res) == false)
1625 return -1;
1626
1627 return towrite;
1628 }
cabfb880 1629 virtual bool InternalWriteError() APT_OVERRIDE
e3fbd54c
JAK
1630 {
1631 char const * const errmsg = LZ4F_getErrorName(res);
1632
1633 return filefd->FileFdError("LZ4F: %s %s (%zu: %s)", filefd->FileName.c_str(), _("Write error"), res, errmsg);
1634 }
cabfb880 1635 virtual bool InternalStream() const APT_OVERRIDE { return true; }
e3fbd54c 1636
cabfb880 1637 virtual bool InternalFlush() APT_OVERRIDE
e3fbd54c
JAK
1638 {
1639 return backend.Flush();
1640 }
1641
cabfb880 1642 virtual bool InternalClose(std::string const &) APT_OVERRIDE
e3fbd54c
JAK
1643 {
1644 /* Reset variables */
1645 res = 0;
40940e63 1646 next_to_load = APT_BUFFER_SIZE;
e3fbd54c
JAK
1647
1648 if (cctx != nullptr)
1649 {
f43dd439
DK
1650 if (filefd->Failed() == false)
1651 {
1652 res = LZ4F_compressEnd(cctx, lz4_buffer.buffer, lz4_buffer.buffersize_max, nullptr);
1653 if (LZ4F_isError(res) || backend.Write(lz4_buffer.buffer, res) == false)
1654 return false;
1655 if (!backend.Flush())
1656 return false;
1657 }
e3fbd54c
JAK
1658 if (!backend.Close())
1659 return false;
1660
1661 res = LZ4F_freeCompressionContext(cctx);
1662 cctx = nullptr;
1663 }
1664
1665 if (dctx != nullptr)
1666 {
1667 res = LZ4F_freeDecompressionContext(dctx);
1668 dctx = nullptr;
1669 }
6f35be91
DK
1670 if (backend.IsOpen())
1671 {
1672 backend.Close();
1673 filefd->iFd = -1;
1674 }
e3fbd54c
JAK
1675
1676 return LZ4F_isError(res) == false;
1677 }
1678
1679 explicit Lz4FileFdPrivate(FileFd * const filefd) : FileFdPrivate(filefd), dctx(nullptr), cctx(nullptr) {}
1680 virtual ~Lz4FileFdPrivate() {
1681 InternalClose("");
1682 }
1683#endif
fa89055f
DK
1684};
1685 /*}}}*/
65ac6aad 1686class APT_HIDDEN LzmaFileFdPrivate: public FileFdPrivate { /*{{{*/
4239dbca 1687#ifdef HAVE_LZMA
fa89055f
DK
1688 struct LZMAFILE {
1689 FILE* file;
f43dd439 1690 FileFd * const filefd;
fa89055f
DK
1691 uint8_t buffer[4096];
1692 lzma_stream stream;
1693 lzma_ret err;
1694 bool eof;
1695 bool compressing;
1696
f43dd439 1697 LZMAFILE(FileFd * const fd) : file(nullptr), filefd(fd), eof(false), compressing(false) { buffer[0] = '\0'; }
fa89055f
DK
1698 ~LZMAFILE()
1699 {
f43dd439 1700 if (compressing == true && filefd->Failed() == false)
fa89055f
DK
1701 {
1702 size_t constexpr buffersize = sizeof(buffer)/sizeof(buffer[0]);
1703 while(true)
1704 {
1705 stream.avail_out = buffersize;
1706 stream.next_out = buffer;
1707 err = lzma_code(&stream, LZMA_FINISH);
1708 if (err != LZMA_OK && err != LZMA_STREAM_END)
1709 {
1710 _error->Error("~LZMAFILE: Compress finalisation failed");
1711 break;
1712 }
1713 size_t const n = buffersize - stream.avail_out;
1714 if (n && fwrite(buffer, 1, n, file) != n)
1715 {
1716 _error->Errno("~LZMAFILE",_("Write error"));
1717 break;
1718 }
1719 if (err == LZMA_STREAM_END)
1720 break;
1721 }
1722 }
1723 lzma_end(&stream);
1724 fclose(file);
1725 }
1726 };
1727 LZMAFILE* lzma;
7a68effc
DK
1728 static uint32_t findXZlevel(std::vector<std::string> const &Args)
1729 {
1730 for (auto a = Args.rbegin(); a != Args.rend(); ++a)
1731 if (a->empty() == false && (*a)[0] == '-' && (*a)[1] != '-')
1732 {
1733 auto const number = a->find_last_of("0123456789");
1734 if (number == std::string::npos)
1735 continue;
1736 auto const extreme = a->find("e", number);
1737 uint32_t level = (extreme != std::string::npos) ? LZMA_PRESET_EXTREME : 0;
1738 switch ((*a)[number])
1739 {
1740 case '0': return level | 0;
1741 case '1': return level | 1;
1742 case '2': return level | 2;
1743 case '3': return level | 3;
1744 case '4': return level | 4;
1745 case '5': return level | 5;
1746 case '6': return level | 6;
1747 case '7': return level | 7;
1748 case '8': return level | 8;
1749 case '9': return level | 9;
1750 }
1751 }
1752 return 6;
1753 }
fa89055f 1754public:
cabfb880 1755 virtual bool InternalOpen(int const iFd, unsigned int const Mode) APT_OVERRIDE
fa89055f
DK
1756 {
1757 if ((Mode & FileFd::ReadWrite) == FileFd::ReadWrite)
1758 return filefd->FileFdError("ReadWrite mode is not supported for lzma/xz files %s", filefd->FileName.c_str());
1759
1760 if (lzma == nullptr)
f43dd439 1761 lzma = new LzmaFileFdPrivate::LZMAFILE(filefd);
fa89055f
DK
1762 if ((Mode & FileFd::WriteOnly) == FileFd::WriteOnly)
1763 lzma->file = fdopen(iFd, "w");
1764 else
1765 lzma->file = fdopen(iFd, "r");
1766 filefd->Flags |= FileFd::Compressed;
1767 if (lzma->file == nullptr)
1768 return false;
1769
fa89055f
DK
1770 lzma_stream tmp_stream = LZMA_STREAM_INIT;
1771 lzma->stream = tmp_stream;
1772
1773 if ((Mode & FileFd::WriteOnly) == FileFd::WriteOnly)
1774 {
7a68effc 1775 uint32_t const xzlevel = findXZlevel(compressor.CompressArgs);
fa89055f
DK
1776 if (compressor.Name == "xz")
1777 {
885a1ffd 1778 if (lzma_easy_encoder(&lzma->stream, xzlevel, LZMA_CHECK_CRC64) != LZMA_OK)
fa89055f
DK
1779 return false;
1780 }
1781 else
1782 {
1783 lzma_options_lzma options;
1784 lzma_lzma_preset(&options, xzlevel);
1785 if (lzma_alone_encoder(&lzma->stream, &options) != LZMA_OK)
1786 return false;
1787 }
1788 lzma->compressing = true;
1789 }
1790 else
1791 {
7a68effc 1792 uint64_t const memlimit = UINT64_MAX;
fa89055f
DK
1793 if (compressor.Name == "xz")
1794 {
1795 if (lzma_auto_decoder(&lzma->stream, memlimit, 0) != LZMA_OK)
1796 return false;
1797 }
1798 else
1799 {
1800 if (lzma_alone_decoder(&lzma->stream, memlimit) != LZMA_OK)
1801 return false;
1802 }
1803 lzma->compressing = false;
1804 }
1805 return true;
1806 }
cabfb880 1807 virtual ssize_t InternalUnbufferedRead(void * const To, unsigned long long const Size) APT_OVERRIDE
fa89055f
DK
1808 {
1809 ssize_t Res;
1810 if (lzma->eof == true)
1811 return 0;
1812
1813 lzma->stream.next_out = (uint8_t *) To;
1814 lzma->stream.avail_out = Size;
1815 if (lzma->stream.avail_in == 0)
1816 {
1817 lzma->stream.next_in = lzma->buffer;
1818 lzma->stream.avail_in = fread(lzma->buffer, 1, sizeof(lzma->buffer)/sizeof(lzma->buffer[0]), lzma->file);
1819 }
1820 lzma->err = lzma_code(&lzma->stream, LZMA_RUN);
1821 if (lzma->err == LZMA_STREAM_END)
1822 {
1823 lzma->eof = true;
1824 Res = Size - lzma->stream.avail_out;
1825 }
1826 else if (lzma->err != LZMA_OK)
1827 {
1828 Res = -1;
1829 errno = 0;
1830 }
1831 else
1832 {
1833 Res = Size - lzma->stream.avail_out;
1834 if (Res == 0)
1835 {
1836 // lzma run was okay, but produced no output…
1837 Res = -1;
1838 errno = EINTR;
1839 }
1840 }
1841 return Res;
1842 }
cabfb880 1843 virtual bool InternalReadError() APT_OVERRIDE
fa89055f
DK
1844 {
1845 return filefd->FileFdError("lzma_read: %s (%d)", _("Read error"), lzma->err);
1846 }
cabfb880 1847 virtual ssize_t InternalWrite(void const * const From, unsigned long long const Size) APT_OVERRIDE
fa89055f 1848 {
9a63c3f4 1849 ssize_t Res;
fa89055f
DK
1850 lzma->stream.next_in = (uint8_t *)From;
1851 lzma->stream.avail_in = Size;
1852 lzma->stream.next_out = lzma->buffer;
1853 lzma->stream.avail_out = sizeof(lzma->buffer)/sizeof(lzma->buffer[0]);
1854 lzma->err = lzma_code(&lzma->stream, LZMA_RUN);
1855 if (lzma->err != LZMA_OK)
1856 return -1;
1857 size_t const n = sizeof(lzma->buffer)/sizeof(lzma->buffer[0]) - lzma->stream.avail_out;
1858 size_t const m = (n == 0) ? 0 : fwrite(lzma->buffer, 1, n, lzma->file);
1859 if (m != n)
9a63c3f4
CW
1860 {
1861 Res = -1;
1862 errno = 0;
1863 }
fa89055f 1864 else
9a63c3f4
CW
1865 {
1866 Res = Size - lzma->stream.avail_in;
1867 if (Res == 0)
1868 {
1869 // lzma run was okay, but produced no output…
1870 Res = -1;
1871 errno = EINTR;
1872 }
1873 }
1874 return Res;
fa89055f 1875 }
cabfb880 1876 virtual bool InternalWriteError() APT_OVERRIDE
fa89055f
DK
1877 {
1878 return filefd->FileFdError("lzma_write: %s (%d)", _("Write error"), lzma->err);
1879 }
cabfb880
DK
1880 virtual bool InternalStream() const APT_OVERRIDE { return true; }
1881 virtual bool InternalClose(std::string const &) APT_OVERRIDE
fa89055f
DK
1882 {
1883 delete lzma;
1884 lzma = nullptr;
1885 return true;
1886 }
1887
11755147 1888 explicit LzmaFileFdPrivate(FileFd * const filefd) : FileFdPrivate(filefd), lzma(nullptr) {}
fa89055f 1889 virtual ~LzmaFileFdPrivate() { InternalClose(""); }
4239dbca 1890#endif
fa89055f
DK
1891};
1892 /*}}}*/
65ac6aad 1893class APT_HIDDEN PipedFileFdPrivate: public FileFdPrivate /*{{{*/
fa89055f
DK
1894/* if we don't have a specific class dealing with library calls, we (un)compress
1895 by executing a specified binary and pipe in/out what we need */
1896{
1897public:
cabfb880 1898 virtual bool InternalOpen(int const, unsigned int const Mode) APT_OVERRIDE
fa89055f
DK
1899 {
1900 // collect zombies here in case we reopen
1901 if (compressor_pid > 0)
1902 ExecWait(compressor_pid, "FileFdCompressor", true);
1903
1904 if ((Mode & FileFd::ReadWrite) == FileFd::ReadWrite)
1905 return filefd->FileFdError("ReadWrite mode is not supported for file %s", filefd->FileName.c_str());
cc9745a0
DK
1906 if (compressor.Binary == "false")
1907 return filefd->FileFdError("libapt has inbuilt support for the %s compression,"
1908 " but was forced to ignore it in favor of an external binary – which isn't installed.", compressor.Name.c_str());
4239dbca 1909
fa89055f
DK
1910 bool const Comp = (Mode & FileFd::WriteOnly) == FileFd::WriteOnly;
1911 if (Comp == false)
1912 {
1913 // Handle 'decompression' of empty files
1914 struct stat Buf;
1915 fstat(filefd->iFd, &Buf);
1916 if (Buf.st_size == 0 && S_ISFIFO(Buf.st_mode) == false)
1917 return true;
1918
1919 // We don't need the file open - instead let the compressor open it
1920 // as he properly knows better how to efficiently read from 'his' file
1921 if (filefd->FileName.empty() == false)
1922 {
1923 close(filefd->iFd);
1924 filefd->iFd = -1;
1925 }
1926 }
1927
1928 // Create a data pipe
1929 int Pipe[2] = {-1,-1};
1930 if (pipe(Pipe) != 0)
1931 return filefd->FileFdErrno("pipe",_("Failed to create subprocess IPC"));
1932 for (int J = 0; J != 2; J++)
1933 SetCloseExec(Pipe[J],true);
1934
1935 compressed_fd = filefd->iFd;
1d68256d 1936 set_is_pipe(true);
fa89055f
DK
1937
1938 if (Comp == true)
1939 filefd->iFd = Pipe[1];
1940 else
1941 filefd->iFd = Pipe[0];
1942
1943 // The child..
1944 compressor_pid = ExecFork();
1945 if (compressor_pid == 0)
1946 {
1947 if (Comp == true)
1948 {
1949 dup2(compressed_fd,STDOUT_FILENO);
1950 dup2(Pipe[0],STDIN_FILENO);
1951 }
1952 else
1953 {
1954 if (compressed_fd != -1)
1955 dup2(compressed_fd,STDIN_FILENO);
1956 dup2(Pipe[1],STDOUT_FILENO);
1957 }
1958 int const nullfd = open("/dev/null", O_WRONLY);
1959 if (nullfd != -1)
1960 {
1961 dup2(nullfd,STDERR_FILENO);
1962 close(nullfd);
1963 }
1964
1965 SetCloseExec(STDOUT_FILENO,false);
1966 SetCloseExec(STDIN_FILENO,false);
1967
1968 std::vector<char const*> Args;
1969 Args.push_back(compressor.Binary.c_str());
1970 std::vector<std::string> const * const addArgs =
1971 (Comp == true) ? &(compressor.CompressArgs) : &(compressor.UncompressArgs);
1972 for (std::vector<std::string>::const_iterator a = addArgs->begin();
1973 a != addArgs->end(); ++a)
1974 Args.push_back(a->c_str());
1975 if (Comp == false && filefd->FileName.empty() == false)
1976 {
1977 // commands not needing arguments, do not need to be told about using standard output
1978 // in reality, only testcases with tools like cat, rev, rot13, … are able to trigger this
1979 if (compressor.CompressArgs.empty() == false && compressor.UncompressArgs.empty() == false)
1980 Args.push_back("--stdout");
1981 if (filefd->TemporaryFileName.empty() == false)
1982 Args.push_back(filefd->TemporaryFileName.c_str());
1983 else
1984 Args.push_back(filefd->FileName.c_str());
1985 }
1986 Args.push_back(NULL);
1987
1988 execvp(Args[0],(char **)&Args[0]);
1989 cerr << _("Failed to exec compressor ") << Args[0] << endl;
1990 _exit(100);
1991 }
1992 if (Comp == true)
1993 close(Pipe[0]);
1994 else
1995 close(Pipe[1]);
1996
1997 return true;
1998 }
cabfb880 1999 virtual ssize_t InternalUnbufferedRead(void * const To, unsigned long long const Size) APT_OVERRIDE
fa89055f
DK
2000 {
2001 return read(filefd->iFd, To, Size);
2002 }
cabfb880 2003 virtual ssize_t InternalWrite(void const * const From, unsigned long long const Size) APT_OVERRIDE
fa89055f
DK
2004 {
2005 return write(filefd->iFd, From, Size);
2006 }
cabfb880 2007 virtual bool InternalClose(std::string const &) APT_OVERRIDE
fa89055f
DK
2008 {
2009 bool Ret = true;
bdc42211
DK
2010 if (filefd->iFd != -1)
2011 {
2012 close(filefd->iFd);
2013 filefd->iFd = -1;
2014 }
fa89055f
DK
2015 if (compressor_pid > 0)
2016 Ret &= ExecWait(compressor_pid, "FileFdCompressor", true);
2017 compressor_pid = -1;
2018 return Ret;
2019 }
11755147 2020 explicit PipedFileFdPrivate(FileFd * const filefd) : FileFdPrivate(filefd) {}
fa89055f
DK
2021 virtual ~PipedFileFdPrivate() { InternalClose(""); }
2022};
2023 /*}}}*/
65ac6aad 2024class APT_HIDDEN DirectFileFdPrivate: public FileFdPrivate /*{{{*/
fa89055f
DK
2025{
2026public:
cabfb880
DK
2027 virtual bool InternalOpen(int const, unsigned int const) APT_OVERRIDE { return true; }
2028 virtual ssize_t InternalUnbufferedRead(void * const To, unsigned long long const Size) APT_OVERRIDE
fa89055f
DK
2029 {
2030 return read(filefd->iFd, To, Size);
2031 }
cabfb880 2032 virtual ssize_t InternalWrite(void const * const From, unsigned long long const Size) APT_OVERRIDE
fa89055f 2033 {
f63123c3 2034 // files opened read+write are strange and only really "supported" for direct files
83e22e26 2035 if (buffer.size() != 0)
f63123c3 2036 {
83e22e26
JAK
2037 lseek(filefd->iFd, -buffer.size(), SEEK_CUR);
2038 buffer.reset();
f63123c3 2039 }
fa89055f
DK
2040 return write(filefd->iFd, From, Size);
2041 }
cabfb880 2042 virtual bool InternalSeek(unsigned long long const To) APT_OVERRIDE
fa89055f
DK
2043 {
2044 off_t const res = lseek(filefd->iFd, To, SEEK_SET);
2045 if (res != (off_t)To)
2046 return filefd->FileFdError("Unable to seek to %llu", To);
2047 seekpos = To;
83e22e26 2048 buffer.reset();
fa89055f
DK
2049 return true;
2050 }
cabfb880 2051 virtual bool InternalSkip(unsigned long long Over) APT_OVERRIDE
fa89055f 2052 {
83e22e26 2053 if (Over >= buffer.size())
f63123c3 2054 {
83e22e26
JAK
2055 Over -= buffer.size();
2056 buffer.reset();
f63123c3
DK
2057 }
2058 else
2059 {
83e22e26 2060 buffer.bufferstart += Over;
f63123c3
DK
2061 return true;
2062 }
2063 if (Over == 0)
2064 return true;
fa89055f
DK
2065 off_t const res = lseek(filefd->iFd, Over, SEEK_CUR);
2066 if (res < 0)
2067 return filefd->FileFdError("Unable to seek ahead %llu",Over);
2068 seekpos = res;
2069 return true;
2070 }
cabfb880 2071 virtual bool InternalTruncate(unsigned long long const To) APT_OVERRIDE
fa89055f 2072 {
83e22e26 2073 if (buffer.size() != 0)
f63123c3
DK
2074 {
2075 unsigned long long const seekpos = lseek(filefd->iFd, 0, SEEK_CUR);
83e22e26
JAK
2076 if ((seekpos - buffer.size()) >= To)
2077 buffer.reset();
f63123c3 2078 else if (seekpos >= To)
83e22e26 2079 buffer.bufferend = (To - seekpos) + buffer.bufferstart;
f63123c3 2080 else
83e22e26 2081 buffer.reset();
f63123c3 2082 }
fa89055f
DK
2083 if (ftruncate(filefd->iFd, To) != 0)
2084 return filefd->FileFdError("Unable to truncate to %llu",To);
2085 return true;
2086 }
cabfb880 2087 virtual unsigned long long InternalTell() APT_OVERRIDE
fa89055f 2088 {
83e22e26 2089 return lseek(filefd->iFd,0,SEEK_CUR) - buffer.size();
fa89055f 2090 }
cabfb880 2091 virtual unsigned long long InternalSize() APT_OVERRIDE
fa89055f
DK
2092 {
2093 return filefd->FileSize();
2094 }
cabfb880
DK
2095 virtual bool InternalClose(std::string const &) APT_OVERRIDE { return true; }
2096 virtual bool InternalAlwaysAutoClose() const APT_OVERRIDE { return false; }
fa89055f 2097
11755147 2098 explicit DirectFileFdPrivate(FileFd * const filefd) : FileFdPrivate(filefd) {}
fa89055f 2099 virtual ~DirectFileFdPrivate() { InternalClose(""); }
4239dbca
DK
2100};
2101 /*}}}*/
6c55f07a
DK
2102// FileFd Constructors /*{{{*/
2103FileFd::FileFd(std::string FileName,unsigned int const Mode,unsigned long AccessMode) : iFd(-1), Flags(0), d(NULL)
2104{
2105 Open(FileName,Mode, None, AccessMode);
2106}
2107FileFd::FileFd(std::string FileName,unsigned int const Mode, CompressMode Compress, unsigned long AccessMode) : iFd(-1), Flags(0), d(NULL)
2108{
2109 Open(FileName,Mode, Compress, AccessMode);
2110}
2111FileFd::FileFd() : iFd(-1), Flags(AutoClose), d(NULL) {}
2112FileFd::FileFd(int const Fd, unsigned int const Mode, CompressMode Compress) : iFd(-1), Flags(0), d(NULL)
2113{
2114 OpenDescriptor(Fd, Mode, Compress);
2115}
2116FileFd::FileFd(int const Fd, bool const AutoClose) : iFd(-1), Flags(0), d(NULL)
2117{
2118 OpenDescriptor(Fd, ReadWrite, None, AutoClose);
2119}
2120 /*}}}*/
13d87e2e 2121// FileFd::Open - Open a file /*{{{*/
578bfd0a
AL
2122// ---------------------------------------------------------------------
2123/* The most commonly used open mode combinations are given with Mode */
e5f3f8c1 2124bool FileFd::Open(string FileName,unsigned int const Mode,CompressMode Compress, unsigned long const AccessMode)
578bfd0a 2125{
257e8d66 2126 if (Mode == ReadOnlyGzip)
e5f3f8c1 2127 return Open(FileName, ReadOnly, Gzip, AccessMode);
257e8d66 2128
468720c5 2129 if (Compress == Auto && (Mode & WriteOnly) == WriteOnly)
ae635e3c 2130 return FileFdError("Autodetection on %s only works in ReadOnly openmode!", FileName.c_str());
257e8d66 2131
468720c5
DK
2132 std::vector<APT::Configuration::Compressor> const compressors = APT::Configuration::getCompressors();
2133 std::vector<APT::Configuration::Compressor>::const_iterator compressor = compressors.begin();
2134 if (Compress == Auto)
2135 {
468720c5
DK
2136 for (; compressor != compressors.end(); ++compressor)
2137 {
e788a834 2138 std::string file = FileName + compressor->Extension;
468720c5
DK
2139 if (FileExists(file) == false)
2140 continue;
2141 FileName = file;
468720c5
DK
2142 break;
2143 }
2144 }
2145 else if (Compress == Extension)
2146 {
52b47296
DK
2147 std::string::size_type const found = FileName.find_last_of('.');
2148 std::string ext;
2149 if (found != std::string::npos)
2150 {
2151 ext = FileName.substr(found);
2152 if (ext == ".new" || ext == ".bak")
2153 {
2154 std::string::size_type const found2 = FileName.find_last_of('.', found - 1);
2155 if (found2 != std::string::npos)
2156 ext = FileName.substr(found2, found - found2);
2157 else
2158 ext.clear();
2159 }
2160 }
aee1aac6
DK
2161 for (; compressor != compressors.end(); ++compressor)
2162 if (ext == compressor->Extension)
2163 break;
2164 // no matching extension - assume uncompressed (imagine files like 'example.org_Packages')
2165 if (compressor == compressors.end())
2166 for (compressor = compressors.begin(); compressor != compressors.end(); ++compressor)
2167 if (compressor->Name == ".")
468720c5 2168 break;
468720c5 2169 }
aee1aac6 2170 else
468720c5
DK
2171 {
2172 std::string name;
2173 switch (Compress)
2174 {
aee1aac6 2175 case None: name = "."; break;
468720c5
DK
2176 case Gzip: name = "gzip"; break;
2177 case Bzip2: name = "bzip2"; break;
2178 case Lzma: name = "lzma"; break;
2179 case Xz: name = "xz"; break;
e3fbd54c 2180 case Lz4: name = "lz4"; break;
aee1aac6
DK
2181 case Auto:
2182 case Extension:
52b47296 2183 // Unreachable
ae635e3c 2184 return FileFdError("Opening File %s in None, Auto or Extension should be already handled?!?", FileName.c_str());
468720c5
DK
2185 }
2186 for (; compressor != compressors.end(); ++compressor)
2187 if (compressor->Name == name)
2188 break;
aee1aac6 2189 if (compressor == compressors.end())
ae635e3c 2190 return FileFdError("Can't find a configured compressor %s for file %s", name.c_str(), FileName.c_str());
468720c5
DK
2191 }
2192
aee1aac6 2193 if (compressor == compressors.end())
ae635e3c 2194 return FileFdError("Can't find a match for specified compressor mode for file %s", FileName.c_str());
e5f3f8c1 2195 return Open(FileName, Mode, *compressor, AccessMode);
aee1aac6 2196}
e5f3f8c1 2197bool FileFd::Open(string FileName,unsigned int const Mode,APT::Configuration::Compressor const &compressor, unsigned long const AccessMode)
aee1aac6
DK
2198{
2199 Close();
aee1aac6
DK
2200 Flags = AutoClose;
2201
2202 if ((Mode & WriteOnly) != WriteOnly && (Mode & (Atomic | Create | Empty | Exclusive)) != 0)
ae635e3c 2203 return FileFdError("ReadOnly mode for %s doesn't accept additional flags!", FileName.c_str());
aee1aac6 2204 if ((Mode & ReadWrite) == 0)
ae635e3c 2205 return FileFdError("No openmode provided in FileFd::Open for %s", FileName.c_str());
468720c5 2206
cd46d4eb
DK
2207 unsigned int OpenMode = Mode;
2208 if (FileName == "/dev/null")
2209 OpenMode = OpenMode & ~(Atomic | Exclusive | Create | Empty);
2210
2211 if ((OpenMode & Atomic) == Atomic)
257e8d66
DK
2212 {
2213 Flags |= Replace;
257e8d66 2214 }
cd46d4eb 2215 else if ((OpenMode & (Exclusive | Create)) == (Exclusive | Create))
257e8d66
DK
2216 {
2217 // for atomic, this will be done by rename in Close()
ce1f3a2c 2218 RemoveFile("FileFd::Open", FileName);
257e8d66 2219 }
cd46d4eb 2220 if ((OpenMode & Empty) == Empty)
578bfd0a 2221 {
257e8d66
DK
2222 struct stat Buf;
2223 if (lstat(FileName.c_str(),&Buf) == 0 && S_ISLNK(Buf.st_mode))
ce1f3a2c 2224 RemoveFile("FileFd::Open", FileName);
257e8d66 2225 }
c4fc2fd7 2226
561f860a 2227 int fileflags = 0;
cd46d4eb 2228 #define if_FLAGGED_SET(FLAG, MODE) if ((OpenMode & FLAG) == FLAG) fileflags |= MODE
561f860a
DK
2229 if_FLAGGED_SET(ReadWrite, O_RDWR);
2230 else if_FLAGGED_SET(ReadOnly, O_RDONLY);
2231 else if_FLAGGED_SET(WriteOnly, O_WRONLY);
4a9db827 2232
561f860a
DK
2233 if_FLAGGED_SET(Create, O_CREAT);
2234 if_FLAGGED_SET(Empty, O_TRUNC);
2235 if_FLAGGED_SET(Exclusive, O_EXCL);
561f860a 2236 #undef if_FLAGGED_SET
52b47296 2237
cd46d4eb 2238 if ((OpenMode & Atomic) == Atomic)
7335eebe
AGM
2239 {
2240 char *name = strdup((FileName + ".XXXXXX").c_str());
2241
dc545c0b 2242 if((iFd = mkstemp(name)) == -1)
7335eebe
AGM
2243 {
2244 free(name);
98b69f9d 2245 return FileFdErrno("mkstemp", "Could not create temporary file for %s", FileName.c_str());
7335eebe
AGM
2246 }
2247
2248 TemporaryFileName = string(name);
7335eebe 2249 free(name);
dc545c0b 2250
230e69d7
DK
2251 // umask() will always set the umask and return the previous value, so
2252 // we first set the umask and then reset it to the old value
2253 mode_t const CurrentUmask = umask(0);
2254 umask(CurrentUmask);
2255 // calculate the actual file permissions (just like open/creat)
2256 mode_t const FilePermissions = (AccessMode & ~CurrentUmask);
2257
2258 if(fchmod(iFd, FilePermissions) == -1)
dc545c0b 2259 return FileFdErrno("fchmod", "Could not change permissions for temporary file %s", TemporaryFileName.c_str());
7335eebe 2260 }
468720c5 2261 else
230e69d7 2262 iFd = open(FileName.c_str(), fileflags, AccessMode);
468720c5 2263
b711c01e 2264 this->FileName = FileName;
cd46d4eb 2265 if (iFd == -1 || OpenInternDescriptor(OpenMode, compressor) == false)
561f860a 2266 {
468720c5 2267 if (iFd != -1)
fc81e8f2 2268 {
561f860a
DK
2269 close (iFd);
2270 iFd = -1;
fc81e8f2 2271 }
ae635e3c 2272 return FileFdErrno("open",_("Could not open file %s"), FileName.c_str());
257e8d66 2273 }
578bfd0a 2274
13d87e2e
AL
2275 SetCloseExec(iFd,true);
2276 return true;
578bfd0a 2277}
257e8d66
DK
2278 /*}}}*/
2279// FileFd::OpenDescriptor - Open a filedescriptor /*{{{*/
52b47296 2280bool FileFd::OpenDescriptor(int Fd, unsigned int const Mode, CompressMode Compress, bool AutoClose)
aee1aac6
DK
2281{
2282 std::vector<APT::Configuration::Compressor> const compressors = APT::Configuration::getCompressors();
2283 std::vector<APT::Configuration::Compressor>::const_iterator compressor = compressors.begin();
2284 std::string name;
bce778a3
MV
2285
2286 // compat with the old API
2287 if (Mode == ReadOnlyGzip && Compress == None)
2288 Compress = Gzip;
2289
aee1aac6
DK
2290 switch (Compress)
2291 {
2292 case None: name = "."; break;
2293 case Gzip: name = "gzip"; break;
2294 case Bzip2: name = "bzip2"; break;
2295 case Lzma: name = "lzma"; break;
2296 case Xz: name = "xz"; break;
e3fbd54c 2297 case Lz4: name = "lz4"; break;
aee1aac6
DK
2298 case Auto:
2299 case Extension:
f97bb523
DK
2300 if (AutoClose == true && Fd != -1)
2301 close(Fd);
ae635e3c 2302 return FileFdError("Opening Fd %d in Auto or Extension compression mode is not supported", Fd);
aee1aac6
DK
2303 }
2304 for (; compressor != compressors.end(); ++compressor)
2305 if (compressor->Name == name)
2306 break;
2307 if (compressor == compressors.end())
f97bb523
DK
2308 {
2309 if (AutoClose == true && Fd != -1)
2310 close(Fd);
ae635e3c 2311 return FileFdError("Can't find a configured compressor %s for file %s", name.c_str(), FileName.c_str());
f97bb523 2312 }
aee1aac6
DK
2313 return OpenDescriptor(Fd, Mode, *compressor, AutoClose);
2314}
52b47296 2315bool FileFd::OpenDescriptor(int Fd, unsigned int const Mode, APT::Configuration::Compressor const &compressor, bool AutoClose)
144c0969
JAK
2316{
2317 Close();
2318 Flags = (AutoClose) ? FileFd::AutoClose : 0;
84baaae9 2319 iFd = Fd;
b711c01e 2320 this->FileName = "";
84baaae9 2321 if (OpenInternDescriptor(Mode, compressor) == false)
468720c5 2322 {
f97bb523 2323 if (iFd != -1 && (
84baaae9 2324 (Flags & Compressed) == Compressed ||
f97bb523
DK
2325 AutoClose == true))
2326 {
468720c5 2327 close (iFd);
f97bb523
DK
2328 iFd = -1;
2329 }
2330 return FileFdError(_("Could not open file descriptor %d"), Fd);
144c0969 2331 }
144c0969 2332 return true;
468720c5 2333}
52b47296 2334bool FileFd::OpenInternDescriptor(unsigned int const Mode, APT::Configuration::Compressor const &compressor)
468720c5 2335{
84baaae9
DK
2336 if (iFd == -1)
2337 return false;
ff477ee1 2338
fa89055f
DK
2339 if (d != nullptr)
2340 d->InternalClose(FileName);
2341
2342 if (d == nullptr)
2343 {
2344 if (false)
2345 /* dummy so that the rest can be 'else if's */;
2346#define APT_COMPRESS_INIT(NAME, CONSTRUCTOR) \
2347 else if (compressor.Name == NAME) \
2348 d = new CONSTRUCTOR(this)
69d6988a 2349#ifdef HAVE_ZLIB
fa89055f 2350 APT_COMPRESS_INIT("gzip", GzipFileFdPrivate);
69d6988a
DK
2351#endif
2352#ifdef HAVE_BZ2
fa89055f 2353 APT_COMPRESS_INIT("bzip2", Bz2FileFdPrivate);
69d6988a 2354#endif
7f350a37 2355#ifdef HAVE_LZMA
fa89055f
DK
2356 APT_COMPRESS_INIT("xz", LzmaFileFdPrivate);
2357 APT_COMPRESS_INIT("lzma", LzmaFileFdPrivate);
7f350a37 2358#endif
e3fbd54c
JAK
2359#ifdef HAVE_LZ4
2360 APT_COMPRESS_INIT("lz4", Lz4FileFdPrivate);
2361#endif
69d6988a 2362#undef APT_COMPRESS_INIT
fa89055f
DK
2363 else if (compressor.Name == "." || compressor.Binary.empty() == true)
2364 d = new DirectFileFdPrivate(this);
2365 else
2366 d = new PipedFileFdPrivate(this);
69d6988a 2367
88749b5d
JAK
2368 if (Mode & BufferedWrite)
2369 d = new BufferedWriteFileFdPrivate(d);
2370
1d68256d
JAK
2371 d->set_openmode(Mode);
2372 d->set_compressor(compressor);
fa89055f 2373 if ((Flags & AutoClose) != AutoClose && d->InternalAlwaysAutoClose())
84baaae9
DK
2374 {
2375 // Need to duplicate fd here or gz/bz2 close for cleanup will close the fd as well
2376 int const internFd = dup(iFd);
2377 if (internFd == -1)
2378 return FileFdErrno("OpenInternDescriptor", _("Could not open file descriptor %d"), iFd);
2379 iFd = internFd;
2380 }
561f860a 2381 }
fa89055f 2382 return d->InternalOpen(iFd, Mode);
144c0969 2383}
578bfd0a 2384 /*}}}*/
8e06abb2 2385// FileFd::~File - Closes the file /*{{{*/
578bfd0a
AL
2386// ---------------------------------------------------------------------
2387/* If the proper modes are selected then we close the Fd and possibly
2388 unlink the file on error. */
8e06abb2 2389FileFd::~FileFd()
578bfd0a
AL
2390{
2391 Close();
500400fe 2392 if (d != NULL)
fa89055f 2393 d->InternalClose(FileName);
96ab3c6f
MV
2394 delete d;
2395 d = NULL;
578bfd0a
AL
2396}
2397 /*}}}*/
8e06abb2 2398// FileFd::Read - Read a bit of the file /*{{{*/
578bfd0a 2399// ---------------------------------------------------------------------
1e3f4083 2400/* We are careful to handle interruption by a signal while reading
b0db36b1 2401 gracefully. */
650faab0 2402bool FileFd::Read(void *To,unsigned long long Size,unsigned long long *Actual)
578bfd0a 2403{
02c38073 2404 if (d == nullptr || Failed())
fa89055f 2405 return false;
39e77e45 2406 ssize_t Res = 1;
b0db36b1 2407 errno = 0;
f604cf55
AL
2408 if (Actual != 0)
2409 *Actual = 0;
699b209e 2410 *((char *)To) = '\0';
39e77e45 2411 while (Res > 0 && Size > 0)
578bfd0a 2412 {
fa89055f 2413 Res = d->InternalRead(To, Size);
b711c01e 2414
b0db36b1
AL
2415 if (Res < 0)
2416 {
b711c01e 2417 if (errno == EINTR)
c4b113e6
DK
2418 {
2419 // trick the while-loop into running again
2420 Res = 1;
2421 errno = 0;
b711c01e 2422 continue;
c4b113e6 2423 }
fa89055f 2424 return d->InternalReadError();
b0db36b1 2425 }
578bfd0a 2426
b0db36b1
AL
2427 To = (char *)To + Res;
2428 Size -= Res;
ff477ee1 2429 if (d != NULL)
1d68256d 2430 d->set_seekpos(d->get_seekpos() + Res);
f604cf55
AL
2431 if (Actual != 0)
2432 *Actual += Res;
b0db36b1 2433 }
b0db36b1
AL
2434
2435 if (Size == 0)
2436 return true;
2437
ddc1d8d0 2438 // Eof handling
f604cf55 2439 if (Actual != 0)
ddc1d8d0
AL
2440 {
2441 Flags |= HitEof;
2442 return true;
2443 }
ae635e3c
DK
2444
2445 return FileFdError(_("read, still have %llu to read but none left"), Size);
578bfd0a
AL
2446}
2447 /*}}}*/
032bd56f
DK
2448// FileFd::ReadLine - Read a complete line from the file /*{{{*/
2449// ---------------------------------------------------------------------
fa89055f 2450/* Beware: This method can be quite slow for big buffers on UNcompressed
032bd56f
DK
2451 files because of the naive implementation! */
2452char* FileFd::ReadLine(char *To, unsigned long long const Size)
2453{
699b209e 2454 *To = '\0';
02c38073 2455 if (d == nullptr || Failed())
fa89055f
DK
2456 return nullptr;
2457 return d->InternalReadLine(To, Size);
032bd56f
DK
2458}
2459 /*}}}*/
766761fd
JAK
2460// FileFd::Flush - Flush the file /*{{{*/
2461bool FileFd::Flush()
2462{
02c38073
DK
2463 if (Failed())
2464 return false;
766761fd
JAK
2465 if (d == nullptr)
2466 return true;
2467
2468 return d->InternalFlush();
2469}
2470 /*}}}*/
8e06abb2 2471// FileFd::Write - Write to the file /*{{{*/
650faab0 2472bool FileFd::Write(const void *From,unsigned long long Size)
578bfd0a 2473{
02c38073 2474 if (d == nullptr || Failed())
fa89055f 2475 return false;
5df91bc7 2476 ssize_t Res = 1;
b0db36b1 2477 errno = 0;
5df91bc7 2478 while (Res > 0 && Size > 0)
578bfd0a 2479 {
fa89055f 2480 Res = d->InternalWrite(From, Size);
9a63c3f4 2481
b0db36b1 2482 if (Res < 0)
9a63c3f4
CW
2483 {
2484 if (errno == EINTR)
2485 {
2486 // trick the while-loop into running again
2487 Res = 1;
2488 errno = 0;
2489 continue;
2490 }
fa89055f 2491 return d->InternalWriteError();
9a63c3f4 2492 }
fa89055f 2493
cf4ff3b7 2494 From = (char const *)From + Res;
b0db36b1 2495 Size -= Res;
ff477ee1 2496 if (d != NULL)
1d68256d 2497 d->set_seekpos(d->get_seekpos() + Res);
578bfd0a 2498 }
fa89055f 2499
b0db36b1
AL
2500 if (Size == 0)
2501 return true;
ae635e3c
DK
2502
2503 return FileFdError(_("write, still have %llu to write but couldn't"), Size);
d68d65ad
DK
2504}
2505bool FileFd::Write(int Fd, const void *From, unsigned long long Size)
2506{
5df91bc7 2507 ssize_t Res = 1;
d68d65ad 2508 errno = 0;
5df91bc7 2509 while (Res > 0 && Size > 0)
d68d65ad
DK
2510 {
2511 Res = write(Fd,From,Size);
2512 if (Res < 0 && errno == EINTR)
2513 continue;
2514 if (Res < 0)
2515 return _error->Errno("write",_("Write error"));
2516
cf4ff3b7 2517 From = (char const *)From + Res;
d68d65ad
DK
2518 Size -= Res;
2519 }
d68d65ad
DK
2520
2521 if (Size == 0)
2522 return true;
2523
2524 return _error->Error(_("write, still have %llu to write but couldn't"), Size);
578bfd0a
AL
2525}
2526 /*}}}*/
8e06abb2 2527// FileFd::Seek - Seek in the file /*{{{*/
650faab0 2528bool FileFd::Seek(unsigned long long To)
578bfd0a 2529{
02c38073 2530 if (d == nullptr || Failed())
fa89055f 2531 return false;
bb93178b 2532 Flags &= ~HitEof;
fa89055f 2533 return d->InternalSeek(To);
727f18af
AL
2534}
2535 /*}}}*/
fa89055f 2536// FileFd::Skip - Skip over data in the file /*{{{*/
650faab0 2537bool FileFd::Skip(unsigned long long Over)
727f18af 2538{
02c38073 2539 if (d == nullptr || Failed())
fa89055f
DK
2540 return false;
2541 return d->InternalSkip(Over);
6d5dd02a
AL
2542}
2543 /*}}}*/
fa89055f 2544// FileFd::Truncate - Truncate the file /*{{{*/
650faab0 2545bool FileFd::Truncate(unsigned long long To)
6d5dd02a 2546{
02c38073 2547 if (d == nullptr || Failed())
fa89055f 2548 return false;
ad5051ef
DK
2549 // truncating /dev/null is always successful - as we get an error otherwise
2550 if (To == 0 && FileName == "/dev/null")
2551 return true;
fa89055f 2552 return d->InternalTruncate(To);
578bfd0a
AL
2553}
2554 /*}}}*/
7f25bdff
AL
2555// FileFd::Tell - Current seek position /*{{{*/
2556// ---------------------------------------------------------------------
2557/* */
650faab0 2558unsigned long long FileFd::Tell()
7f25bdff 2559{
02c38073 2560 if (d == nullptr || Failed())
fa89055f
DK
2561 return false;
2562 off_t const Res = d->InternalTell();
7f25bdff 2563 if (Res == (off_t)-1)
ae635e3c 2564 FileFdErrno("lseek","Failed to determine the current file position");
1d68256d 2565 d->set_seekpos(Res);
7f25bdff
AL
2566 return Res;
2567}
2568 /*}}}*/
8190b07a 2569static bool StatFileFd(char const * const msg, int const iFd, std::string const &FileName, struct stat &Buf, FileFdPrivate * const d) /*{{{*/
578bfd0a 2570{
1d68256d 2571 bool ispipe = (d != NULL && d->get_is_pipe() == true);
6008b79a
DK
2572 if (ispipe == false)
2573 {
2574 if (fstat(iFd,&Buf) != 0)
8190b07a
DK
2575 // higher-level code will generate more meaningful messages,
2576 // even translated this would be meaningless for users
2577 return _error->Errno("fstat", "Unable to determine %s for fd %i", msg, iFd);
003c40d3
DK
2578 if (FileName.empty() == false)
2579 ispipe = S_ISFIFO(Buf.st_mode);
6008b79a 2580 }
699b209e
DK
2581
2582 // for compressor pipes st_size is undefined and at 'best' zero
6008b79a 2583 if (ispipe == true)
699b209e
DK
2584 {
2585 // we set it here, too, as we get the info here for free
2586 // in theory the Open-methods should take care of it already
ff477ee1 2587 if (d != NULL)
1d68256d 2588 d->set_is_pipe(true);
699b209e 2589 if (stat(FileName.c_str(), &Buf) != 0)
8190b07a
DK
2590 return _error->Errno("fstat", "Unable to determine %s for file %s", msg, FileName.c_str());
2591 }
2592 return true;
2593}
2594 /*}}}*/
2595// FileFd::FileSize - Return the size of the file /*{{{*/
2596unsigned long long FileFd::FileSize()
2597{
2598 struct stat Buf;
2599 if (StatFileFd("file size", iFd, FileName, Buf, d) == false)
2600 {
2601 Flags |= Fail;
2602 return 0;
699b209e 2603 }
4260fd39
DK
2604 return Buf.st_size;
2605}
2606 /*}}}*/
8190b07a
DK
2607// FileFd::ModificationTime - Return the time of last touch /*{{{*/
2608time_t FileFd::ModificationTime()
2609{
2610 struct stat Buf;
2611 if (StatFileFd("modification time", iFd, FileName, Buf, d) == false)
2612 {
2613 Flags |= Fail;
2614 return 0;
2615 }
2616 return Buf.st_mtime;
2617}
2618 /*}}}*/
4260fd39 2619// FileFd::Size - Return the size of the content in the file /*{{{*/
650faab0 2620unsigned long long FileFd::Size()
4260fd39 2621{
fa89055f 2622 if (d == nullptr)
02c38073 2623 return 0;
fa89055f 2624 return d->InternalSize();
578bfd0a
AL
2625}
2626 /*}}}*/
8e06abb2 2627// FileFd::Close - Close the file if the close flag is set /*{{{*/
578bfd0a
AL
2628// ---------------------------------------------------------------------
2629/* */
8e06abb2 2630bool FileFd::Close()
578bfd0a 2631{
f43dd439 2632 if (Failed() == false && Flush() == false)
766761fd 2633 return false;
032bd56f
DK
2634 if (iFd == -1)
2635 return true;
2636
578bfd0a
AL
2637 bool Res = true;
2638 if ((Flags & AutoClose) == AutoClose)
d13c2d3f 2639 {
500400fe
DK
2640 if ((Flags & Compressed) != Compressed && iFd > 0 && close(iFd) != 0)
2641 Res &= _error->Errno("close",_("Problem closing the file %s"), FileName.c_str());
2da8aae5
JAK
2642 }
2643
2644 if (d != NULL)
2645 {
fa89055f 2646 Res &= d->InternalClose(FileName);
2da8aae5
JAK
2647 delete d;
2648 d = NULL;
d13c2d3f 2649 }
3010fb0e 2650
d3aac32e 2651 if ((Flags & Replace) == Replace) {
fc5db01b 2652 if (Failed() == false && rename(TemporaryFileName.c_str(), FileName.c_str()) != 0)
62d073d9
DK
2653 Res &= _error->Errno("rename",_("Problem renaming the file %s to %s"), TemporaryFileName.c_str(), FileName.c_str());
2654
fd3b761e 2655 FileName = TemporaryFileName; // for the unlink() below.
257e8d66 2656 TemporaryFileName.clear();
3010fb0e 2657 }
62d073d9
DK
2658
2659 iFd = -1;
2660
578bfd0a
AL
2661 if ((Flags & Fail) == Fail && (Flags & DelOnFail) == DelOnFail &&
2662 FileName.empty() == false)
ce1f3a2c 2663 Res &= RemoveFile("FileFd::Close", FileName);
3010fb0e 2664
fbb89d94
DK
2665 if (Res == false)
2666 Flags |= Fail;
578bfd0a
AL
2667 return Res;
2668}
2669 /*}}}*/
b2e465d6
AL
2670// FileFd::Sync - Sync the file /*{{{*/
2671// ---------------------------------------------------------------------
2672/* */
2673bool FileFd::Sync()
2674{
b2e465d6 2675 if (fsync(iFd) != 0)
ae635e3c
DK
2676 return FileFdErrno("sync",_("Problem syncing the file"));
2677 return true;
2678}
2679 /*}}}*/
2680// FileFd::FileFdErrno - set Fail and call _error->Errno *{{{*/
2681bool FileFd::FileFdErrno(const char *Function, const char *Description,...)
2682{
2683 Flags |= Fail;
2684 va_list args;
2685 size_t msgSize = 400;
2686 int const errsv = errno;
196d590a
DK
2687 bool retry;
2688 do {
ae635e3c 2689 va_start(args,Description);
196d590a 2690 retry = _error->InsertErrno(GlobalError::ERROR, Function, Description, args, errsv, msgSize);
ae635e3c 2691 va_end(args);
196d590a 2692 } while (retry);
ae635e3c
DK
2693 return false;
2694}
2695 /*}}}*/
2696// FileFd::FileFdError - set Fail and call _error->Error *{{{*/
2697bool FileFd::FileFdError(const char *Description,...) {
2698 Flags |= Fail;
2699 va_list args;
2700 size_t msgSize = 400;
196d590a
DK
2701 bool retry;
2702 do {
ae635e3c 2703 va_start(args,Description);
196d590a 2704 retry = _error->Insert(GlobalError::ERROR, Description, args, msgSize);
ae635e3c 2705 va_end(args);
196d590a 2706 } while (retry);
ae635e3c 2707 return false;
b2e465d6
AL
2708}
2709 /*}}}*/
fa89055f 2710gzFile FileFd::gzFd() { /*{{{*/
7f350a37 2711#ifdef HAVE_ZLIB
fa89055f
DK
2712 GzipFileFdPrivate * const gzipd = dynamic_cast<GzipFileFdPrivate*>(d);
2713 if (gzipd == nullptr)
2714 return nullptr;
2715 else
2716 return gzipd->gz;
7f350a37 2717#else
fa89055f 2718 return nullptr;
7f350a37
DK
2719#endif
2720}
fa89055f 2721 /*}}}*/
8d01b9d6 2722
f8aba23f 2723// Glob - wrapper around "glob()" /*{{{*/
8d01b9d6
MV
2724std::vector<std::string> Glob(std::string const &pattern, int flags)
2725{
2726 std::vector<std::string> result;
2727 glob_t globbuf;
ec4835a1
ÁGM
2728 int glob_res;
2729 unsigned int i;
8d01b9d6
MV
2730
2731 glob_res = glob(pattern.c_str(), flags, NULL, &globbuf);
2732
2733 if (glob_res != 0)
2734 {
2735 if(glob_res != GLOB_NOMATCH) {
2736 _error->Errno("glob", "Problem with glob");
2737 return result;
2738 }
2739 }
2740
2741 // append results
2742 for(i=0;i<globbuf.gl_pathc;i++)
2743 result.push_back(string(globbuf.gl_pathv[i]));
2744
2745 globfree(&globbuf);
2746 return result;
2747}
2748 /*}}}*/
f8aba23f 2749std::string GetTempDir() /*{{{*/
68e01721
MV
2750{
2751 const char *tmpdir = getenv("TMPDIR");
2752
2753#ifdef P_tmpdir
2754 if (!tmpdir)
2755 tmpdir = P_tmpdir;
2756#endif
2757
68e01721 2758 struct stat st;
0d303f17 2759 if (!tmpdir || strlen(tmpdir) == 0 || // tmpdir is set
dd6da7d2
DK
2760 stat(tmpdir, &st) != 0 || (st.st_mode & S_IFDIR) == 0) // exists and is directory
2761 tmpdir = "/tmp";
2762 else if (geteuid() != 0 && // root can do everything anyway
2763 faccessat(-1, tmpdir, R_OK | W_OK | X_OK, AT_EACCESS | AT_SYMLINK_NOFOLLOW) != 0) // current user has rwx access to directory
68e01721
MV
2764 tmpdir = "/tmp";
2765
2766 return string(tmpdir);
dd6da7d2
DK
2767}
2768std::string GetTempDir(std::string const &User)
2769{
2770 // no need/possibility to drop privs
2771 if(getuid() != 0 || User.empty() || User == "root")
2772 return GetTempDir();
2773
2774 struct passwd const * const pw = getpwnam(User.c_str());
2775 if (pw == NULL)
2776 return GetTempDir();
2777
226c0f64
DK
2778 gid_t const old_euid = geteuid();
2779 gid_t const old_egid = getegid();
dd6da7d2
DK
2780 if (setegid(pw->pw_gid) != 0)
2781 _error->Errno("setegid", "setegid %u failed", pw->pw_gid);
2782 if (seteuid(pw->pw_uid) != 0)
2783 _error->Errno("seteuid", "seteuid %u failed", pw->pw_uid);
2784
2785 std::string const tmp = GetTempDir();
2786
226c0f64
DK
2787 if (seteuid(old_euid) != 0)
2788 _error->Errno("seteuid", "seteuid %u failed", old_euid);
2789 if (setegid(old_egid) != 0)
2790 _error->Errno("setegid", "setegid %u failed", old_egid);
dd6da7d2
DK
2791
2792 return tmp;
68e01721 2793}
f8aba23f 2794 /*}}}*/
c9443c01 2795FileFd* GetTempFile(std::string const &Prefix, bool ImmediateUnlink, FileFd * const TmpFd) /*{{{*/
0d29b9d4
MV
2796{
2797 char fn[512];
c9443c01 2798 FileFd * const Fd = TmpFd == NULL ? new FileFd() : TmpFd;
0d29b9d4 2799
c9443c01
DK
2800 std::string const tempdir = GetTempDir();
2801 snprintf(fn, sizeof(fn), "%s/%s.XXXXXX",
0d29b9d4 2802 tempdir.c_str(), Prefix.c_str());
c9443c01 2803 int const fd = mkstemp(fn);
0d29b9d4
MV
2804 if(ImmediateUnlink)
2805 unlink(fn);
c9443c01 2806 if (fd < 0)
0d29b9d4
MV
2807 {
2808 _error->Errno("GetTempFile",_("Unable to mkstemp %s"), fn);
2809 return NULL;
2810 }
c9443c01 2811 if (!Fd->OpenDescriptor(fd, FileFd::ReadWrite, FileFd::None, true))
0d29b9d4
MV
2812 {
2813 _error->Errno("GetTempFile",_("Unable to write to %s"),fn);
2814 return NULL;
2815 }
0d29b9d4
MV
2816 return Fd;
2817}
f8aba23f
DK
2818 /*}}}*/
2819bool Rename(std::string From, std::string To) /*{{{*/
c1409d1b
MV
2820{
2821 if (rename(From.c_str(),To.c_str()) != 0)
2822 {
2823 _error->Error(_("rename failed, %s (%s -> %s)."),strerror(errno),
2824 From.c_str(),To.c_str());
2825 return false;
f8aba23f 2826 }
c1409d1b
MV
2827 return true;
2828}
f8aba23f
DK
2829 /*}}}*/
2830bool Popen(const char* Args[], FileFd &Fd, pid_t &Child, FileFd::OpenMode Mode)/*{{{*/
7ad2a347
MV
2831{
2832 int fd;
2833 if (Mode != FileFd::ReadOnly && Mode != FileFd::WriteOnly)
2834 return _error->Error("Popen supports ReadOnly (x)or WriteOnly mode only");
2835
2836 int Pipe[2] = {-1, -1};
2837 if(pipe(Pipe) != 0)
7ad2a347 2838 return _error->Errno("pipe", _("Failed to create subprocess IPC"));
5e49cbb7 2839
7ad2a347
MV
2840 std::set<int> keep_fds;
2841 keep_fds.insert(Pipe[0]);
2842 keep_fds.insert(Pipe[1]);
2843 Child = ExecFork(keep_fds);
2844 if(Child < 0)
2845 return _error->Errno("fork", "Failed to fork");
2846 if(Child == 0)
2847 {
2848 if(Mode == FileFd::ReadOnly)
2849 {
2850 close(Pipe[0]);
2851 fd = Pipe[1];
2852 }
2853 else if(Mode == FileFd::WriteOnly)
2854 {
2855 close(Pipe[1]);
2856 fd = Pipe[0];
2857 }
2858
2859 if(Mode == FileFd::ReadOnly)
2860 {
2861 dup2(fd, 1);
2862 dup2(fd, 2);
2863 } else if(Mode == FileFd::WriteOnly)
2864 dup2(fd, 0);
2865
2866 execv(Args[0], (char**)Args);
2867 _exit(100);
2868 }
2869 if(Mode == FileFd::ReadOnly)
2870 {
2871 close(Pipe[1]);
2872 fd = Pipe[0];
8f5b67ae
DK
2873 }
2874 else if(Mode == FileFd::WriteOnly)
7ad2a347
MV
2875 {
2876 close(Pipe[0]);
2877 fd = Pipe[1];
2878 }
8f5b67ae
DK
2879 else
2880 return _error->Error("Popen supports ReadOnly (x)or WriteOnly mode only");
7ad2a347
MV
2881 Fd.OpenDescriptor(fd, Mode, FileFd::None, true);
2882
2883 return true;
2884}
f8aba23f
DK
2885 /*}}}*/
2886bool DropPrivileges() /*{{{*/
fc1a78d8 2887{
8f45798d
DK
2888 if(_config->FindB("Debug::NoDropPrivs", false) == true)
2889 return true;
2890
2891#if __gnu_linux__
2892#if defined(PR_SET_NO_NEW_PRIVS) && ( PR_SET_NO_NEW_PRIVS != 38 )
2893#error "PR_SET_NO_NEW_PRIVS is defined, but with a different value than expected!"
2894#endif
2895 // see prctl(2), needs linux3.5 at runtime - magic constant to avoid it at buildtime
2896 int ret = prctl(38, 1, 0, 0, 0);
2897 // ignore EINVAL - kernel is too old to understand the option
2898 if(ret < 0 && errno != EINVAL)
2899 _error->Warning("PR_SET_NO_NEW_PRIVS failed with %i", ret);
2900#endif
2901
990dd78a
DK
2902 // empty setting disables privilege dropping - this also ensures
2903 // backward compatibility, see bug #764506
2904 const std::string toUser = _config->Find("APT::Sandbox::User");
514a25cb 2905 if (toUser.empty() || toUser == "root")
990dd78a
DK
2906 return true;
2907
ebca2f25
DK
2908 // a lot can go wrong trying to drop privileges completely,
2909 // so ideally we would like to verify that we have done it –
2910 // but the verify asks for too much in case of fakeroot (and alike)
2911 // [Specific checks can be overridden with dedicated options]
2912 bool const VerifySandboxing = _config->FindB("APT::Sandbox::Verify", false);
2913
f1e3c8f0 2914 // uid will be 0 in the end, but gid might be different anyway
8f45798d
DK
2915 uid_t const old_uid = getuid();
2916 gid_t const old_gid = getgid();
fc1a78d8 2917
5f2047ec
JAK
2918 if (old_uid != 0)
2919 return true;
3927c6da 2920
b8dae9a1 2921 struct passwd *pw = getpwnam(toUser.c_str());
fc1a78d8 2922 if (pw == NULL)
b8dae9a1 2923 return _error->Error("No user %s, can not drop rights", toUser.c_str());
3927c6da 2924
f1e3c8f0 2925 // Do not change the order here, it might break things
5a326439 2926 // Get rid of all our supplementary groups first
3b084f06 2927 if (setgroups(1, &pw->pw_gid))
3927c6da
MV
2928 return _error->Errno("setgroups", "Failed to setgroups");
2929
5a326439
JAK
2930 // Now change the group ids to the new user
2931#ifdef HAVE_SETRESGID
2932 if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) != 0)
2933 return _error->Errno("setresgid", "Failed to set new group ids");
2934#else
3927c6da 2935 if (setegid(pw->pw_gid) != 0)
5f2047ec
JAK
2936 return _error->Errno("setegid", "Failed to setegid");
2937
fc1a78d8
MV
2938 if (setgid(pw->pw_gid) != 0)
2939 return _error->Errno("setgid", "Failed to setgid");
5a326439 2940#endif
5f2047ec 2941
5a326439
JAK
2942 // Change the user ids to the new user
2943#ifdef HAVE_SETRESUID
2944 if (setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) != 0)
2945 return _error->Errno("setresuid", "Failed to set new user ids");
2946#else
fc1a78d8
MV
2947 if (setuid(pw->pw_uid) != 0)
2948 return _error->Errno("setuid", "Failed to setuid");
5f2047ec
JAK
2949 if (seteuid(pw->pw_uid) != 0)
2950 return _error->Errno("seteuid", "Failed to seteuid");
5a326439 2951#endif
5f2047ec 2952
ebca2f25
DK
2953 // disabled by default as fakeroot doesn't implement getgroups currently (#806521)
2954 if (VerifySandboxing == true || _config->FindB("APT::Sandbox::Verify::Groups", false) == true)
2955 {
2956 // Verify that the user isn't still in any supplementary groups
2957 long const ngroups_max = sysconf(_SC_NGROUPS_MAX);
2958 std::unique_ptr<gid_t[]> gidlist(new gid_t[ngroups_max]);
2959 if (unlikely(gidlist == NULL))
2960 return _error->Error("Allocation of a list of size %lu for getgroups failed", ngroups_max);
2961 ssize_t gidlist_nr;
2962 if ((gidlist_nr = getgroups(ngroups_max, gidlist.get())) < 0)
2963 return _error->Errno("getgroups", "Could not get new groups (%lu)", ngroups_max);
2964 for (ssize_t i = 0; i < gidlist_nr; ++i)
2965 if (gidlist[i] != pw->pw_gid)
2966 return _error->Error("Could not switch group, user %s is still in group %d", toUser.c_str(), gidlist[i]);
2967 }
2968
2969 // enabled by default as all fakeroot-lookalikes should fake that accordingly
2970 if (VerifySandboxing == true || _config->FindB("APT::Sandbox::Verify::IDs", true) == true)
2971 {
2972 // Verify that gid, egid, uid, and euid changed
2973 if (getgid() != pw->pw_gid)
2974 return _error->Error("Could not switch group");
2975 if (getegid() != pw->pw_gid)
2976 return _error->Error("Could not switch effective group");
2977 if (getuid() != pw->pw_uid)
2978 return _error->Error("Could not switch user");
2979 if (geteuid() != pw->pw_uid)
2980 return _error->Error("Could not switch effective user");
5f2047ec 2981
550ab420 2982#ifdef HAVE_GETRESUID
ebca2f25
DK
2983 // verify that the saved set-user-id was changed as well
2984 uid_t ruid = 0;
2985 uid_t euid = 0;
2986 uid_t suid = 0;
2987 if (getresuid(&ruid, &euid, &suid))
2988 return _error->Errno("getresuid", "Could not get saved set-user-ID");
2989 if (suid != pw->pw_uid)
2990 return _error->Error("Could not switch saved set-user-ID");
550ab420
JAK
2991#endif
2992
2993#ifdef HAVE_GETRESGID
ebca2f25
DK
2994 // verify that the saved set-group-id was changed as well
2995 gid_t rgid = 0;
2996 gid_t egid = 0;
2997 gid_t sgid = 0;
2998 if (getresgid(&rgid, &egid, &sgid))
2999 return _error->Errno("getresuid", "Could not get saved set-group-ID");
3000 if (sgid != pw->pw_gid)
3001 return _error->Error("Could not switch saved set-group-ID");
550ab420 3002#endif
ebca2f25 3003 }
550ab420 3004
ebca2f25
DK
3005 // disabled as fakeroot doesn't forbid (by design) (re)gaining root from unprivileged
3006 if (VerifySandboxing == true || _config->FindB("APT::Sandbox::Verify::Regain", false) == true)
3007 {
3008 // Check that uid and gid changes do not work anymore
3009 if (pw->pw_gid != old_gid && (setgid(old_gid) != -1 || setegid(old_gid) != -1))
3010 return _error->Error("Could restore a gid to root, privilege dropping did not work");
bdc00df5 3011
ebca2f25
DK
3012 if (pw->pw_uid != old_uid && (setuid(old_uid) != -1 || seteuid(old_uid) != -1))
3013 return _error->Error("Could restore a uid to root, privilege dropping did not work");
3014 }
bdc00df5 3015
fc1a78d8
MV
3016 return true;
3017}
f8aba23f 3018 /*}}}*/