]> git.saurik.com Git - apt.git/blame - apt-pkg/contrib/fileutl.cc
Use some semantically more correct buffer operations
[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 /*}}}*/
3b5421b4
AL
697// SetCloseExec - Set the close on exec flag /*{{{*/
698// ---------------------------------------------------------------------
699/* */
700void SetCloseExec(int Fd,bool Close)
701{
702 if (fcntl(Fd,F_SETFD,(Close == false)?0:FD_CLOEXEC) != 0)
703 {
704 cerr << "FATAL -> Could not set close on exec " << strerror(errno) << endl;
705 exit(100);
706 }
707}
708 /*}}}*/
709// SetNonBlock - Set the nonblocking flag /*{{{*/
710// ---------------------------------------------------------------------
711/* */
712void SetNonBlock(int Fd,bool Block)
713{
0a8a80e5
AL
714 int Flags = fcntl(Fd,F_GETFL) & (~O_NONBLOCK);
715 if (fcntl(Fd,F_SETFL,Flags | ((Block == false)?0:O_NONBLOCK)) != 0)
3b5421b4
AL
716 {
717 cerr << "FATAL -> Could not set non-blocking flag " << strerror(errno) << endl;
718 exit(100);
719 }
720}
721 /*}}}*/
722// WaitFd - Wait for a FD to become readable /*{{{*/
723// ---------------------------------------------------------------------
b2e465d6 724/* This waits for a FD to become readable using select. It is useful for
6d5dd02a
AL
725 applications making use of non-blocking sockets. The timeout is
726 in seconds. */
1084d58a 727bool WaitFd(int Fd,bool write,unsigned long timeout)
3b5421b4
AL
728{
729 fd_set Set;
cc2313b7 730 struct timeval tv;
3b5421b4
AL
731 FD_ZERO(&Set);
732 FD_SET(Fd,&Set);
6d5dd02a
AL
733 tv.tv_sec = timeout;
734 tv.tv_usec = 0;
1084d58a 735 if (write == true)
b0db36b1
AL
736 {
737 int Res;
738 do
739 {
740 Res = select(Fd+1,0,&Set,0,(timeout != 0?&tv:0));
741 }
742 while (Res < 0 && errno == EINTR);
743
744 if (Res <= 0)
745 return false;
1084d58a
AL
746 }
747 else
748 {
b0db36b1
AL
749 int Res;
750 do
751 {
752 Res = select(Fd+1,&Set,0,0,(timeout != 0?&tv:0));
753 }
754 while (Res < 0 && errno == EINTR);
755
756 if (Res <= 0)
757 return false;
cc2313b7 758 }
1084d58a 759
3b5421b4
AL
760 return true;
761}
762 /*}}}*/
96ae6de5 763// MergeKeepFdsFromConfiguration - Merge APT::Keep-Fds configuration /*{{{*/
54676e1a 764// ---------------------------------------------------------------------
96ae6de5
MV
765/* This is used to merge the APT::Keep-Fds with the provided KeepFDs
766 * set.
767 */
768void MergeKeepFdsFromConfiguration(std::set<int> &KeepFDs)
e45c4617 769{
e45c4617
MV
770 Configuration::Item const *Opts = _config->Tree("APT::Keep-Fds");
771 if (Opts != 0 && Opts->Child != 0)
772 {
773 Opts = Opts->Child;
774 for (; Opts != 0; Opts = Opts->Next)
775 {
776 if (Opts->Value.empty() == true)
777 continue;
778 int fd = atoi(Opts->Value.c_str());
779 KeepFDs.insert(fd);
780 }
781 }
96ae6de5
MV
782}
783 /*}}}*/
54676e1a
AL
784// ExecFork - Magical fork that sanitizes the context before execing /*{{{*/
785// ---------------------------------------------------------------------
786/* This is used if you want to cleanse the environment for the forked
787 child, it fixes up the important signals and nukes all of the fds,
788 otherwise acts like normal fork. */
75ef8f14 789pid_t ExecFork()
96ae6de5
MV
790{
791 set<int> KeepFDs;
792 // we need to merge the Keep-Fds as external tools like
793 // debconf-apt-progress use it
794 MergeKeepFdsFromConfiguration(KeepFDs);
e45c4617
MV
795 return ExecFork(KeepFDs);
796}
797
798pid_t ExecFork(std::set<int> KeepFDs)
54676e1a
AL
799{
800 // Fork off the process
801 pid_t Process = fork();
802 if (Process < 0)
803 {
804 cerr << "FATAL -> Failed to fork." << endl;
805 exit(100);
806 }
807
808 // Spawn the subprocess
809 if (Process == 0)
810 {
811 // Setup the signals
812 signal(SIGPIPE,SIG_DFL);
813 signal(SIGQUIT,SIG_DFL);
814 signal(SIGINT,SIG_DFL);
815 signal(SIGWINCH,SIG_DFL);
816 signal(SIGCONT,SIG_DFL);
817 signal(SIGTSTP,SIG_DFL);
75ef8f14 818
be4d908f
JAK
819 DIR *dir = opendir("/proc/self/fd");
820 if (dir != NULL)
75ef8f14 821 {
be4d908f
JAK
822 struct dirent *ent;
823 while ((ent = readdir(dir)))
824 {
825 int fd = atoi(ent->d_name);
826 // If fd > 0, it was a fd number and not . or ..
827 if (fd >= 3 && KeepFDs.find(fd) == KeepFDs.end())
828 fcntl(fd,F_SETFD,FD_CLOEXEC);
829 }
830 closedir(dir);
831 } else {
832 long ScOpenMax = sysconf(_SC_OPEN_MAX);
833 // Close all of our FDs - just in case
834 for (int K = 3; K != ScOpenMax; K++)
835 {
836 if(KeepFDs.find(K) == KeepFDs.end())
837 fcntl(K,F_SETFD,FD_CLOEXEC);
838 }
75ef8f14 839 }
54676e1a
AL
840 }
841
842 return Process;
843}
844 /*}}}*/
ddc1d8d0
AL
845// ExecWait - Fancy waitpid /*{{{*/
846// ---------------------------------------------------------------------
2c9a72d1 847/* Waits for the given sub process. If Reap is set then no errors are
ddc1d8d0
AL
848 generated. Otherwise a failed subprocess will generate a proper descriptive
849 message */
3826564e 850bool ExecWait(pid_t Pid,const char *Name,bool Reap)
ddc1d8d0
AL
851{
852 if (Pid <= 1)
853 return true;
854
855 // Wait and collect the error code
856 int Status;
857 while (waitpid(Pid,&Status,0) != Pid)
858 {
859 if (errno == EINTR)
860 continue;
861
862 if (Reap == true)
863 return false;
864
db0db9fe 865 return _error->Error(_("Waited for %s but it wasn't there"),Name);
ddc1d8d0
AL
866 }
867
868
869 // Check for an error code.
870 if (WIFEXITED(Status) == 0 || WEXITSTATUS(Status) != 0)
871 {
872 if (Reap == true)
873 return false;
ab7f4d7c 874 if (WIFSIGNALED(Status) != 0)
40e7fe0e 875 {
ab7f4d7c
MV
876 if( WTERMSIG(Status) == SIGSEGV)
877 return _error->Error(_("Sub-process %s received a segmentation fault."),Name);
878 else
879 return _error->Error(_("Sub-process %s received signal %u."),Name, WTERMSIG(Status));
40e7fe0e 880 }
ddc1d8d0
AL
881
882 if (WIFEXITED(Status) != 0)
b2e465d6 883 return _error->Error(_("Sub-process %s returned an error code (%u)"),Name,WEXITSTATUS(Status));
ddc1d8d0 884
b2e465d6 885 return _error->Error(_("Sub-process %s exited unexpectedly"),Name);
ddc1d8d0
AL
886 }
887
888 return true;
889}
890 /*}}}*/
f8aba23f 891// StartsWithGPGClearTextSignature - Check if a file is Pgp/GPG clearsigned /*{{{*/
fe5804fc 892bool StartsWithGPGClearTextSignature(string const &FileName)
0854ad8b
MV
893{
894 static const char* SIGMSG = "-----BEGIN PGP SIGNED MESSAGE-----\n";
1c89c98a 895 char buffer[strlen(SIGMSG)+1];
0854ad8b
MV
896 FILE* gpg = fopen(FileName.c_str(), "r");
897 if (gpg == NULL)
898 return false;
899
900 char const * const test = fgets(buffer, sizeof(buffer), gpg);
901 fclose(gpg);
902 if (test == NULL || strcmp(buffer, SIGMSG) != 0)
903 return false;
904
905 return true;
906}
f8aba23f 907 /*}}}*/
d84da499
DK
908// ChangeOwnerAndPermissionOfFile - set file attributes to requested values /*{{{*/
909bool ChangeOwnerAndPermissionOfFile(char const * const requester, char const * const file, char const * const user, char const * const group, mode_t const mode)
910{
911 if (strcmp(file, "/dev/null") == 0)
912 return true;
913 bool Res = true;
914 if (getuid() == 0 && strlen(user) != 0 && strlen(group) != 0) // if we aren't root, we can't chown, so don't try it
915 {
916 // ensure the file is owned by root and has good permissions
917 struct passwd const * const pw = getpwnam(user);
918 struct group const * const gr = getgrnam(group);
919 if (pw != NULL && gr != NULL && chown(file, pw->pw_uid, gr->gr_gid) != 0)
920 Res &= _error->WarningE(requester, "chown to %s:%s of file %s failed", user, group, file);
921 }
922 if (chmod(file, mode) != 0)
923 Res &= _error->WarningE(requester, "chmod 0%o of file %s failed", mode, file);
924 return Res;
925}
926 /*}}}*/
0854ad8b 927
38dba8cd 928struct APT_HIDDEN simple_buffer { /*{{{*/
5bdba2ca 929 size_t buffersize_max = 0;
38dba8cd
JAK
930 unsigned long long bufferstart = 0;
931 unsigned long long bufferend = 0;
5bdba2ca
JAK
932 char *buffer = nullptr;
933
934 simple_buffer() {
935 reset(4096);
936 }
937 ~simple_buffer() {
ab16ead7 938 delete[] buffer;
5bdba2ca 939 }
38dba8cd 940
ea58d39e 941 const char *get() const { return buffer + bufferstart; }
38dba8cd 942 char *get() { return buffer + bufferstart; }
f1b9bf7a
JAK
943 const char *getend() const { return buffer + bufferend; }
944 char *getend() { return buffer + bufferend; }
ea58d39e 945 bool empty() const { return bufferend <= bufferstart; }
c368b3ab 946 bool full() const { return bufferend == buffersize_max; }
f1b9bf7a 947 unsigned long long free() const { return buffersize_max - bufferend; }
ea58d39e 948 unsigned long long size() const { return bufferend-bufferstart; }
5bdba2ca
JAK
949 void reset(size_t size)
950 {
951 if (size > buffersize_max) {
952 delete[] buffer;
953 buffersize_max = size;
954 buffer = new char[size];
955 }
956 reset();
957 }
38dba8cd
JAK
958 void reset() { bufferend = bufferstart = 0; }
959 ssize_t read(void *to, unsigned long long requested_size) APT_MUSTCHECK
960 {
961 if (size() < requested_size)
962 requested_size = size();
963 memcpy(to, buffer + bufferstart, requested_size);
964 bufferstart += requested_size;
965 if (bufferstart == bufferend)
966 bufferstart = bufferend = 0;
967 return requested_size;
968 }
c368b3ab
JAK
969 ssize_t write(const void *from, unsigned long long requested_size) APT_MUSTCHECK
970 {
47fcfff8
JAK
971 if (free() < requested_size)
972 requested_size = free();
973 memcpy(getend(), from, requested_size);
c368b3ab
JAK
974 bufferend += requested_size;
975 if (bufferstart == bufferend)
976 bufferstart = bufferend = 0;
977 return requested_size;
978 }
38dba8cd
JAK
979};
980 /*}}}*/
981
65ac6aad 982class APT_HIDDEN FileFdPrivate { /*{{{*/
88749b5d 983 friend class BufferedWriteFileFdPrivate;
fa89055f
DK
984protected:
985 FileFd * const filefd;
38dba8cd 986 simple_buffer buffer;
fa89055f
DK
987 int compressed_fd;
988 pid_t compressor_pid;
989 bool is_pipe;
990 APT::Configuration::Compressor compressor;
991 unsigned int openmode;
992 unsigned long long seekpos;
1d68256d
JAK
993public:
994
83e22e26 995 explicit FileFdPrivate(FileFd * const pfilefd) : filefd(pfilefd),
fa89055f
DK
996 compressed_fd(-1), compressor_pid(-1), is_pipe(false),
997 openmode(0), seekpos(0) {};
1d68256d
JAK
998 virtual APT::Configuration::Compressor get_compressor() const
999 {
1000 return compressor;
1001 }
1002 virtual void set_compressor(APT::Configuration::Compressor const &compressor)
1003 {
1004 this->compressor = compressor;
1005 }
1006 virtual unsigned int get_openmode() const
1007 {
1008 return openmode;
1009 }
1010 virtual void set_openmode(unsigned int openmode)
1011 {
1012 this->openmode = openmode;
1013 }
1014 virtual bool get_is_pipe() const
1015 {
1016 return is_pipe;
1017 }
1018 virtual void set_is_pipe(bool is_pipe)
1019 {
1020 this->is_pipe = is_pipe;
1021 }
1022 virtual unsigned long long get_seekpos() const
1023 {
1024 return seekpos;
1025 }
1026 virtual void set_seekpos(unsigned long long seekpos)
1027 {
1028 this->seekpos = seekpos;
1029 }
fa89055f
DK
1030
1031 virtual bool InternalOpen(int const iFd, unsigned int const Mode) = 0;
f63123c3 1032 ssize_t InternalRead(void * To, unsigned long long Size)
fa89055f 1033 {
83e22e26
JAK
1034 // Drain the buffer if needed.
1035 if (buffer.empty() == false)
fa89055f 1036 {
83e22e26 1037 return buffer.read(To, Size);
fa89055f 1038 }
83e22e26 1039 return InternalUnbufferedRead(To, Size);
f63123c3
DK
1040 }
1041 virtual ssize_t InternalUnbufferedRead(void * const To, unsigned long long const Size) = 0;
1042 virtual bool InternalReadError() { return filefd->FileFdErrno("read",_("Read error")); }
1043 virtual char * InternalReadLine(char * To, unsigned long long Size)
1044 {
1045 if (unlikely(Size == 0))
1046 return nullptr;
01152444 1047 // Read one byte less than buffer size to have space for trailing 0.
f63123c3 1048 --Size;
01152444 1049
f63123c3
DK
1050 char * const InitialTo = To;
1051
01152444 1052 while (Size > 0) {
83e22e26 1053 if (buffer.empty() == true)
f63123c3 1054 {
83e22e26 1055 buffer.reset();
f63123c3 1056 unsigned long long actualread = 0;
72ed5f14 1057 if (filefd->Read(buffer.getend(), buffer.free(), &actualread) == false)
f63123c3 1058 return nullptr;
83e22e26
JAK
1059 buffer.bufferend = actualread;
1060 if (buffer.size() == 0)
f63123c3
DK
1061 {
1062 if (To == InitialTo)
1063 return nullptr;
1064 break;
1065 }
1066 filefd->Flags &= ~FileFd::HitEof;
1067 }
1068
83e22e26 1069 unsigned long long const OutputSize = std::min(Size, buffer.size());
b3db9d81 1070 char const * const newline = static_cast<char const * const>(memchr(buffer.get(), '\n', OutputSize));
a9024b1b
JAK
1071 // Read until end of line or up to Size bytes from the buffer.
1072 unsigned long long actualread = buffer.read(To,
1073 (newline != nullptr)
1074 ? (newline - buffer.get()) + 1
1075 : OutputSize);
1076 To += actualread;
1077 Size -= actualread;
f63123c3 1078 if (newline != nullptr)
f63123c3 1079 break;
01152444 1080 }
f63123c3
DK
1081 *To = '\0';
1082 return InitialTo;
fa89055f 1083 }
766761fd
JAK
1084 virtual bool InternalFlush()
1085 {
1086 return true;
1087 }
fa89055f
DK
1088 virtual ssize_t InternalWrite(void const * const From, unsigned long long const Size) = 0;
1089 virtual bool InternalWriteError() { return filefd->FileFdErrno("write",_("Write error")); }
1090 virtual bool InternalSeek(unsigned long long const To)
1091 {
1092 // Our poor man seeking is costly, so try to avoid it
1093 unsigned long long const iseekpos = filefd->Tell();
1094 if (iseekpos == To)
1095 return true;
1096 else if (iseekpos < To)
1097 return filefd->Skip(To - iseekpos);
1098
1099 if ((openmode & FileFd::ReadOnly) != FileFd::ReadOnly)
1100 return filefd->FileFdError("Reopen is only implemented for read-only files!");
1101 InternalClose(filefd->FileName);
1102 if (filefd->iFd != -1)
1103 close(filefd->iFd);
1104 filefd->iFd = -1;
1105 if (filefd->TemporaryFileName.empty() == false)
1106 filefd->iFd = open(filefd->TemporaryFileName.c_str(), O_RDONLY);
1107 else if (filefd->FileName.empty() == false)
1108 filefd->iFd = open(filefd->FileName.c_str(), O_RDONLY);
1109 else
1110 {
1111 if (compressed_fd > 0)
1112 if (lseek(compressed_fd, 0, SEEK_SET) != 0)
1113 filefd->iFd = compressed_fd;
1114 if (filefd->iFd < 0)
1115 return filefd->FileFdError("Reopen is not implemented for pipes opened with FileFd::OpenDescriptor()!");
1116 }
1117
1118 if (filefd->OpenInternDescriptor(openmode, compressor) == false)
1119 return filefd->FileFdError("Seek on file %s because it couldn't be reopened", filefd->FileName.c_str());
1120
83e22e26 1121 buffer.reset();
fa89055f
DK
1122 if (To != 0)
1123 return filefd->Skip(To);
1124
1125 seekpos = To;
1126 return true;
1127 }
1128 virtual bool InternalSkip(unsigned long long Over)
1129 {
1130 unsigned long long constexpr buffersize = 1024;
1131 char buffer[buffersize];
1132 while (Over != 0)
1133 {
1134 unsigned long long toread = std::min(buffersize, Over);
1135 if (filefd->Read(buffer, toread) == false)
1136 return filefd->FileFdError("Unable to seek ahead %llu",Over);
1137 Over -= toread;
1138 }
1139 return true;
1140 }
1141 virtual bool InternalTruncate(unsigned long long const)
1142 {
1143 return filefd->FileFdError("Truncating compressed files is not implemented (%s)", filefd->FileName.c_str());
1144 }
1145 virtual unsigned long long InternalTell()
1146 {
1147 // In theory, we could just return seekpos here always instead of
1148 // seeking around, but not all users of FileFd use always Seek() and co
1149 // so d->seekpos isn't always true and we can just use it as a hint if
1150 // we have nothing else, but not always as an authority…
83e22e26 1151 return seekpos - buffer.size();
fa89055f
DK
1152 }
1153 virtual unsigned long long InternalSize()
1154 {
1155 unsigned long long size = 0;
1156 unsigned long long const oldSeek = filefd->Tell();
1157 unsigned long long constexpr ignoresize = 1024;
1158 char ignore[ignoresize];
1159 unsigned long long read = 0;
1160 do {
1161 if (filefd->Read(ignore, ignoresize, &read) == false)
1162 {
1163 filefd->Seek(oldSeek);
1164 return 0;
1165 }
1166 } while(read != 0);
1167 size = filefd->Tell();
1168 filefd->Seek(oldSeek);
1169 return size;
1170 }
1171 virtual bool InternalClose(std::string const &FileName) = 0;
1172 virtual bool InternalStream() const { return false; }
1173 virtual bool InternalAlwaysAutoClose() const { return true; }
1174
1175 virtual ~FileFdPrivate() {}
1176};
1177 /*}}}*/
88749b5d
JAK
1178class APT_HIDDEN BufferedWriteFileFdPrivate : public FileFdPrivate { /*{{{*/
1179protected:
1180 FileFdPrivate *wrapped;
1181 simple_buffer writebuffer;
1182
1183public:
1184
1185 explicit BufferedWriteFileFdPrivate(FileFdPrivate *Priv) :
1186 FileFdPrivate(Priv->filefd), wrapped(Priv) {};
1187
1188 virtual APT::Configuration::Compressor get_compressor() const override
1189 {
1190 return wrapped->get_compressor();
1191 }
1192 virtual void set_compressor(APT::Configuration::Compressor const &compressor) override
1193 {
1194 return wrapped->set_compressor(compressor);
1195 }
1196 virtual unsigned int get_openmode() const override
1197 {
1198 return wrapped->get_openmode();
1199 }
1200 virtual void set_openmode(unsigned int openmode) override
1201 {
1202 return wrapped->set_openmode(openmode);
1203 }
1204 virtual bool get_is_pipe() const override
1205 {
1206 return wrapped->get_is_pipe();
1207 }
1208 virtual void set_is_pipe(bool is_pipe) override
1209 {
1210 FileFdPrivate::set_is_pipe(is_pipe);
1211 wrapped->set_is_pipe(is_pipe);
1212 }
1213 virtual unsigned long long get_seekpos() const override
1214 {
1215 return wrapped->get_seekpos();
1216 }
1217 virtual void set_seekpos(unsigned long long seekpos) override
1218 {
1219 return wrapped->set_seekpos(seekpos);
1220 }
1221 virtual bool InternalOpen(int const iFd, unsigned int const Mode) override
1222 {
1223 if (InternalFlush() == false)
1224 return false;
1225 return wrapped->InternalOpen(iFd, Mode);
1226 }
1227 virtual ssize_t InternalUnbufferedRead(void * const To, unsigned long long const Size) override
1228 {
1229 if (InternalFlush() == false)
1230 return -1;
1231 return wrapped->InternalUnbufferedRead(To, Size);
1232
1233 }
1234 virtual bool InternalReadError() override
1235 {
1236 return wrapped->InternalReadError();
1237 }
1238 virtual char * InternalReadLine(char * To, unsigned long long Size) override
1239 {
1240 if (InternalFlush() == false)
1241 return nullptr;
1242 return wrapped->InternalReadLine(To, Size);
1243 }
1244 virtual bool InternalFlush() override
1245 {
1f5062f6
JAK
1246 while (writebuffer.empty() == false) {
1247 auto written = wrapped->InternalWrite(writebuffer.get(),
1248 writebuffer.size());
1249 // Ignore interrupted syscalls
1250 if (written < 0 && errno == EINTR)
1251 continue;
1252 if (written < 0)
1996a6a7 1253 return wrapped->InternalWriteError();
88749b5d 1254
1f5062f6 1255 writebuffer.bufferstart += written;
88749b5d
JAK
1256 }
1257
1258 writebuffer.reset();
1259 return true;
1260 }
1261 virtual ssize_t InternalWrite(void const * const From, unsigned long long const Size) override
1262 {
f1828e6b
JAK
1263 // Optimisation: If the buffer is empty and we have more to write than
1264 // would fit in the buffer (or equal number of bytes), write directly.
1265 if (writebuffer.empty() == true && Size >= writebuffer.free())
1266 return wrapped->InternalWrite(From, Size);
1267
1268 // Write as much into the buffer as possible and then flush if needed
070ed1c9 1269 auto written = writebuffer.write(From, Size);
88749b5d 1270
070ed1c9
JAK
1271 if (writebuffer.full() && InternalFlush() == false)
1272 return -1;
88749b5d
JAK
1273
1274 return written;
1275 }
1276 virtual bool InternalWriteError()
1277 {
1278 return wrapped->InternalWriteError();
1279 }
1280 virtual bool InternalSeek(unsigned long long const To)
1281 {
1282 if (InternalFlush() == false)
1283 return false;
1284 return wrapped->InternalSeek(To);
1285 }
1286 virtual bool InternalSkip(unsigned long long Over)
1287 {
1288 if (InternalFlush() == false)
1289 return false;
1290 return wrapped->InternalSkip(Over);
1291 }
1292 virtual bool InternalTruncate(unsigned long long const Size)
1293 {
1294 if (InternalFlush() == false)
1295 return false;
1296 return wrapped->InternalTruncate(Size);
1297 }
1298 virtual unsigned long long InternalTell()
1299 {
1300 if (InternalFlush() == false)
1301 return -1;
1302 return wrapped->InternalTell();
1303 }
1304 virtual unsigned long long InternalSize()
1305 {
1306 if (InternalFlush() == false)
1307 return -1;
1308 return wrapped->InternalSize();
1309 }
1310 virtual bool InternalClose(std::string const &FileName)
1311 {
1312 return wrapped->InternalClose(FileName);
1313 }
1314 virtual bool InternalAlwaysAutoClose() const
1315 {
1316 return wrapped->InternalAlwaysAutoClose();
1317 }
1318 virtual ~BufferedWriteFileFdPrivate()
1319 {
1320 delete wrapped;
1321 }
1322};
1323 /*}}}*/
65ac6aad 1324class APT_HIDDEN GzipFileFdPrivate: public FileFdPrivate { /*{{{*/
4239dbca 1325#ifdef HAVE_ZLIB
fa89055f
DK
1326public:
1327 gzFile gz;
1328 virtual bool InternalOpen(int const iFd, unsigned int const Mode) override
1329 {
1330 if ((Mode & FileFd::ReadWrite) == FileFd::ReadWrite)
1331 gz = gzdopen(iFd, "r+");
1332 else if ((Mode & FileFd::WriteOnly) == FileFd::WriteOnly)
1333 gz = gzdopen(iFd, "w");
1334 else
1335 gz = gzdopen(iFd, "r");
1336 filefd->Flags |= FileFd::Compressed;
1337 return gz != nullptr;
1338 }
f63123c3 1339 virtual ssize_t InternalUnbufferedRead(void * const To, unsigned long long const Size) override
fa89055f
DK
1340 {
1341 return gzread(gz, To, Size);
1342 }
1343 virtual bool InternalReadError() override
1344 {
1345 int err;
1346 char const * const errmsg = gzerror(gz, &err);
1347 if (err != Z_ERRNO)
1348 return filefd->FileFdError("gzread: %s (%d: %s)", _("Read error"), err, errmsg);
1349 return FileFdPrivate::InternalReadError();
1350 }
f63123c3 1351 virtual char * InternalReadLine(char * To, unsigned long long Size) override
fa89055f
DK
1352 {
1353 return gzgets(gz, To, Size);
1354 }
1355 virtual ssize_t InternalWrite(void const * const From, unsigned long long const Size) override
1356 {
1357 return gzwrite(gz,From,Size);
1358 }
1359 virtual bool InternalWriteError() override
1360 {
1361 int err;
1362 char const * const errmsg = gzerror(gz, &err);
1363 if (err != Z_ERRNO)
1364 return filefd->FileFdError("gzwrite: %s (%d: %s)", _("Write error"), err, errmsg);
1365 return FileFdPrivate::InternalWriteError();
1366 }
1367 virtual bool InternalSeek(unsigned long long const To) override
1368 {
1369 off_t const res = gzseek(gz, To, SEEK_SET);
1370 if (res != (off_t)To)
1371 return filefd->FileFdError("Unable to seek to %llu", To);
fa89055f 1372 seekpos = To;
83e22e26 1373 buffer.reset();
fa89055f
DK
1374 return true;
1375 }
1376 virtual bool InternalSkip(unsigned long long Over) override
1377 {
83e22e26 1378 if (Over >= buffer.size())
f63123c3 1379 {
83e22e26
JAK
1380 Over -= buffer.size();
1381 buffer.reset();
f63123c3
DK
1382 }
1383 else
1384 {
83e22e26 1385 buffer.bufferstart += Over;
f63123c3
DK
1386 return true;
1387 }
1388 if (Over == 0)
1389 return true;
fa89055f
DK
1390 off_t const res = gzseek(gz, Over, SEEK_CUR);
1391 if (res < 0)
1392 return filefd->FileFdError("Unable to seek ahead %llu",Over);
1393 seekpos = res;
1394 return true;
1395 }
1396 virtual unsigned long long InternalTell() override
1397 {
83e22e26 1398 return gztell(gz) - buffer.size();
fa89055f
DK
1399 }
1400 virtual unsigned long long InternalSize() override
1401 {
1402 unsigned long long filesize = FileFdPrivate::InternalSize();
1403 // only check gzsize if we are actually a gzip file, just checking for
1404 // "gz" is not sufficient as uncompressed files could be opened with
1405 // gzopen in "direct" mode as well
1406 if (filesize == 0 || gzdirect(gz))
1407 return filesize;
1408
1409 off_t const oldPos = lseek(filefd->iFd, 0, SEEK_CUR);
1410 /* unfortunately zlib.h doesn't provide a gzsize(), so we have to do
1411 * this ourselves; the original (uncompressed) file size is the last 32
1412 * bits of the file */
1413 // FIXME: Size for gz-files is limited by 32bit… no largefile support
1414 if (lseek(filefd->iFd, -4, SEEK_END) < 0)
1415 {
1416 filefd->FileFdErrno("lseek","Unable to seek to end of gzipped file");
1417 return 0;
1418 }
1419 uint32_t size = 0;
1420 if (read(filefd->iFd, &size, 4) != 4)
1421 {
1422 filefd->FileFdErrno("read","Unable to read original size of gzipped file");
1423 return 0;
1424 }
1425 size = le32toh(size);
1426
1427 if (lseek(filefd->iFd, oldPos, SEEK_SET) < 0)
1428 {
1429 filefd->FileFdErrno("lseek","Unable to seek in gzipped file");
1430 return 0;
1431 }
1432 return size;
1433 }
1434 virtual bool InternalClose(std::string const &FileName) override
1435 {
1436 if (gz == nullptr)
1437 return true;
1438 int const e = gzclose(gz);
1439 gz = nullptr;
1440 // gzdclose() on empty files always fails with "buffer error" here, ignore that
1441 if (e != 0 && e != Z_BUF_ERROR)
1442 return _error->Errno("close",_("Problem closing the gzip file %s"), FileName.c_str());
1443 return true;
1444 }
1445
11755147 1446 explicit GzipFileFdPrivate(FileFd * const filefd) : FileFdPrivate(filefd), gz(nullptr) {}
fa89055f 1447 virtual ~GzipFileFdPrivate() { InternalClose(""); }
4239dbca 1448#endif
fa89055f
DK
1449};
1450 /*}}}*/
65ac6aad 1451class APT_HIDDEN Bz2FileFdPrivate: public FileFdPrivate { /*{{{*/
4239dbca 1452#ifdef HAVE_BZ2
fa89055f
DK
1453 BZFILE* bz2;
1454public:
1455 virtual bool InternalOpen(int const iFd, unsigned int const Mode) override
1456 {
1457 if ((Mode & FileFd::ReadWrite) == FileFd::ReadWrite)
1458 bz2 = BZ2_bzdopen(iFd, "r+");
1459 else if ((Mode & FileFd::WriteOnly) == FileFd::WriteOnly)
1460 bz2 = BZ2_bzdopen(iFd, "w");
1461 else
1462 bz2 = BZ2_bzdopen(iFd, "r");
1463 filefd->Flags |= FileFd::Compressed;
1464 return bz2 != nullptr;
1465 }
f63123c3 1466 virtual ssize_t InternalUnbufferedRead(void * const To, unsigned long long const Size) override
fa89055f
DK
1467 {
1468 return BZ2_bzread(bz2, To, Size);
1469 }
1470 virtual bool InternalReadError() override
1471 {
1472 int err;
1473 char const * const errmsg = BZ2_bzerror(bz2, &err);
1474 if (err != BZ_IO_ERROR)
1475 return filefd->FileFdError("BZ2_bzread: %s %s (%d: %s)", filefd->FileName.c_str(), _("Read error"), err, errmsg);
1476 return FileFdPrivate::InternalReadError();
1477 }
1478 virtual ssize_t InternalWrite(void const * const From, unsigned long long const Size) override
1479 {
1480 return BZ2_bzwrite(bz2, (void*)From, Size);
1481 }
1482 virtual bool InternalWriteError() override
1483 {
1484 int err;
1485 char const * const errmsg = BZ2_bzerror(bz2, &err);
1486 if (err != BZ_IO_ERROR)
1487 return filefd->FileFdError("BZ2_bzwrite: %s %s (%d: %s)", filefd->FileName.c_str(), _("Write error"), err, errmsg);
1488 return FileFdPrivate::InternalWriteError();
1489 }
1490 virtual bool InternalStream() const override { return true; }
1491 virtual bool InternalClose(std::string const &) override
1492 {
1493 if (bz2 == nullptr)
1494 return true;
1495 BZ2_bzclose(bz2);
1496 bz2 = nullptr;
1497 return true;
1498 }
1499
11755147 1500 explicit Bz2FileFdPrivate(FileFd * const filefd) : FileFdPrivate(filefd), bz2(nullptr) {}
fa89055f 1501 virtual ~Bz2FileFdPrivate() { InternalClose(""); }
4239dbca 1502#endif
e3fbd54c
JAK
1503};
1504 /*}}}*/
1505class APT_HIDDEN Lz4FileFdPrivate: public FileFdPrivate { /*{{{*/
e3fbd54c
JAK
1506 static constexpr unsigned long long LZ4_HEADER_SIZE = 19;
1507 static constexpr unsigned long long LZ4_FOOTER_SIZE = 4;
1508#ifdef HAVE_LZ4
1509 LZ4F_decompressionContext_t dctx;
1510 LZ4F_compressionContext_t cctx;
1511 LZ4F_errorCode_t res;
1512 FileFd backend;
1513 simple_buffer lz4_buffer;
1514 // Count of bytes that the decompressor expects to read next, or buffer size.
40940e63 1515 size_t next_to_load = APT_BUFFER_SIZE;
e3fbd54c
JAK
1516public:
1517 virtual bool InternalOpen(int const iFd, unsigned int const Mode) override
1518 {
1519 if ((Mode & FileFd::ReadWrite) == FileFd::ReadWrite)
1520 return _error->Error("lz4 only supports write or read mode");
1521
1522 if ((Mode & FileFd::WriteOnly) == FileFd::WriteOnly) {
1523 res = LZ4F_createCompressionContext(&cctx, LZ4F_VERSION);
40940e63 1524 lz4_buffer.reset(LZ4F_compressBound(APT_BUFFER_SIZE, nullptr)
e3fbd54c
JAK
1525 + LZ4_HEADER_SIZE + LZ4_FOOTER_SIZE);
1526 } else {
1527 res = LZ4F_createDecompressionContext(&dctx, LZ4F_VERSION);
40940e63 1528 lz4_buffer.reset(APT_BUFFER_SIZE);
e3fbd54c
JAK
1529 }
1530
1531 filefd->Flags |= FileFd::Compressed;
1532
1533 if (LZ4F_isError(res))
1534 return false;
1535
1536 unsigned int flags = (Mode & (FileFd::WriteOnly|FileFd::ReadOnly));
1537 if (backend.OpenDescriptor(iFd, flags) == false)
1538 return false;
1539
1540 // Write the file header
1541 if ((Mode & FileFd::WriteOnly) == FileFd::WriteOnly)
1542 {
1543 res = LZ4F_compressBegin(cctx, lz4_buffer.buffer, lz4_buffer.buffersize_max, nullptr);
1544 if (LZ4F_isError(res) || backend.Write(lz4_buffer.buffer, res) == false)
1545 return false;
1546 }
1547
1548 return true;
1549 }
1550 virtual ssize_t InternalUnbufferedRead(void * const To, unsigned long long const Size) override
1551 {
1552 /* Keep reading as long as the compressor still wants to read */
1553 while (next_to_load) {
1554 // Fill compressed buffer;
1555 if (lz4_buffer.empty()) {
1556 unsigned long long read;
1557 /* Reset - if LZ4 decompressor wants to read more, allocate more */
1558 lz4_buffer.reset(next_to_load);
1559 if (backend.Read(lz4_buffer.getend(), lz4_buffer.free(), &read) == false)
1560 return -1;
1561 lz4_buffer.bufferend += read;
1562
1563 /* Expected EOF */
1564 if (read == 0) {
1565 res = -1;
1566 return filefd->FileFdError("LZ4F: %s %s",
1567 filefd->FileName.c_str(),
1568 _("Unexpected end of file")), -1;
1569 }
1570 }
1571 // Drain compressed buffer as far as possible.
1572 size_t in = lz4_buffer.size();
1573 size_t out = Size;
1574
1575 res = LZ4F_decompress(dctx, To, &out, lz4_buffer.get(), &in, nullptr);
1576 if (LZ4F_isError(res))
1577 return -1;
1578
1579 next_to_load = res;
1580 lz4_buffer.bufferstart += in;
1581
1582 if (out != 0)
1583 return out;
1584 }
1585
1586 return 0;
1587 }
1588 virtual bool InternalReadError() override
1589 {
1590 char const * const errmsg = LZ4F_getErrorName(res);
1591
1592 return filefd->FileFdError("LZ4F: %s %s (%zu: %s)", filefd->FileName.c_str(), _("Read error"), res, errmsg);
1593 }
1594 virtual ssize_t InternalWrite(void const * const From, unsigned long long const Size) override
1595 {
40940e63 1596 unsigned long long const towrite = std::min(APT_BUFFER_SIZE, Size);
e3fbd54c
JAK
1597
1598 res = LZ4F_compressUpdate(cctx,
1599 lz4_buffer.buffer, lz4_buffer.buffersize_max,
1600 From, towrite, nullptr);
1601
1602 if (LZ4F_isError(res) || backend.Write(lz4_buffer.buffer, res) == false)
1603 return -1;
1604
1605 return towrite;
1606 }
1607 virtual bool InternalWriteError() override
1608 {
1609 char const * const errmsg = LZ4F_getErrorName(res);
1610
1611 return filefd->FileFdError("LZ4F: %s %s (%zu: %s)", filefd->FileName.c_str(), _("Write error"), res, errmsg);
1612 }
1613 virtual bool InternalStream() const override { return true; }
1614
1615 virtual bool InternalFlush() override
1616 {
1617 return backend.Flush();
1618 }
1619
1620 virtual bool InternalClose(std::string const &) override
1621 {
1622 /* Reset variables */
1623 res = 0;
40940e63 1624 next_to_load = APT_BUFFER_SIZE;
e3fbd54c
JAK
1625
1626 if (cctx != nullptr)
1627 {
1628 res = LZ4F_compressEnd(cctx, lz4_buffer.buffer, lz4_buffer.buffersize_max, nullptr);
1629 if (LZ4F_isError(res) || backend.Write(lz4_buffer.buffer, res) == false)
1630 return false;
1631 if (!backend.Flush())
1632 return false;
1633 if (!backend.Close())
1634 return false;
1635
1636 res = LZ4F_freeCompressionContext(cctx);
1637 cctx = nullptr;
1638 }
1639
1640 if (dctx != nullptr)
1641 {
1642 res = LZ4F_freeDecompressionContext(dctx);
1643 dctx = nullptr;
1644 }
1645
1646 return LZ4F_isError(res) == false;
1647 }
1648
1649 explicit Lz4FileFdPrivate(FileFd * const filefd) : FileFdPrivate(filefd), dctx(nullptr), cctx(nullptr) {}
1650 virtual ~Lz4FileFdPrivate() {
1651 InternalClose("");
1652 }
1653#endif
fa89055f
DK
1654};
1655 /*}}}*/
65ac6aad 1656class APT_HIDDEN LzmaFileFdPrivate: public FileFdPrivate { /*{{{*/
4239dbca 1657#ifdef HAVE_LZMA
fa89055f
DK
1658 struct LZMAFILE {
1659 FILE* file;
1660 uint8_t buffer[4096];
1661 lzma_stream stream;
1662 lzma_ret err;
1663 bool eof;
1664 bool compressing;
1665
1666 LZMAFILE() : file(nullptr), eof(false), compressing(false) { buffer[0] = '\0'; }
1667 ~LZMAFILE()
1668 {
1669 if (compressing == true)
1670 {
1671 size_t constexpr buffersize = sizeof(buffer)/sizeof(buffer[0]);
1672 while(true)
1673 {
1674 stream.avail_out = buffersize;
1675 stream.next_out = buffer;
1676 err = lzma_code(&stream, LZMA_FINISH);
1677 if (err != LZMA_OK && err != LZMA_STREAM_END)
1678 {
1679 _error->Error("~LZMAFILE: Compress finalisation failed");
1680 break;
1681 }
1682 size_t const n = buffersize - stream.avail_out;
1683 if (n && fwrite(buffer, 1, n, file) != n)
1684 {
1685 _error->Errno("~LZMAFILE",_("Write error"));
1686 break;
1687 }
1688 if (err == LZMA_STREAM_END)
1689 break;
1690 }
1691 }
1692 lzma_end(&stream);
1693 fclose(file);
1694 }
1695 };
1696 LZMAFILE* lzma;
7a68effc
DK
1697 static uint32_t findXZlevel(std::vector<std::string> const &Args)
1698 {
1699 for (auto a = Args.rbegin(); a != Args.rend(); ++a)
1700 if (a->empty() == false && (*a)[0] == '-' && (*a)[1] != '-')
1701 {
1702 auto const number = a->find_last_of("0123456789");
1703 if (number == std::string::npos)
1704 continue;
1705 auto const extreme = a->find("e", number);
1706 uint32_t level = (extreme != std::string::npos) ? LZMA_PRESET_EXTREME : 0;
1707 switch ((*a)[number])
1708 {
1709 case '0': return level | 0;
1710 case '1': return level | 1;
1711 case '2': return level | 2;
1712 case '3': return level | 3;
1713 case '4': return level | 4;
1714 case '5': return level | 5;
1715 case '6': return level | 6;
1716 case '7': return level | 7;
1717 case '8': return level | 8;
1718 case '9': return level | 9;
1719 }
1720 }
1721 return 6;
1722 }
fa89055f
DK
1723public:
1724 virtual bool InternalOpen(int const iFd, unsigned int const Mode) override
1725 {
1726 if ((Mode & FileFd::ReadWrite) == FileFd::ReadWrite)
1727 return filefd->FileFdError("ReadWrite mode is not supported for lzma/xz files %s", filefd->FileName.c_str());
1728
1729 if (lzma == nullptr)
1730 lzma = new LzmaFileFdPrivate::LZMAFILE;
1731 if ((Mode & FileFd::WriteOnly) == FileFd::WriteOnly)
1732 lzma->file = fdopen(iFd, "w");
1733 else
1734 lzma->file = fdopen(iFd, "r");
1735 filefd->Flags |= FileFd::Compressed;
1736 if (lzma->file == nullptr)
1737 return false;
1738
fa89055f
DK
1739 lzma_stream tmp_stream = LZMA_STREAM_INIT;
1740 lzma->stream = tmp_stream;
1741
1742 if ((Mode & FileFd::WriteOnly) == FileFd::WriteOnly)
1743 {
7a68effc 1744 uint32_t const xzlevel = findXZlevel(compressor.CompressArgs);
fa89055f
DK
1745 if (compressor.Name == "xz")
1746 {
885a1ffd 1747 if (lzma_easy_encoder(&lzma->stream, xzlevel, LZMA_CHECK_CRC64) != LZMA_OK)
fa89055f
DK
1748 return false;
1749 }
1750 else
1751 {
1752 lzma_options_lzma options;
1753 lzma_lzma_preset(&options, xzlevel);
1754 if (lzma_alone_encoder(&lzma->stream, &options) != LZMA_OK)
1755 return false;
1756 }
1757 lzma->compressing = true;
1758 }
1759 else
1760 {
7a68effc 1761 uint64_t const memlimit = UINT64_MAX;
fa89055f
DK
1762 if (compressor.Name == "xz")
1763 {
1764 if (lzma_auto_decoder(&lzma->stream, memlimit, 0) != LZMA_OK)
1765 return false;
1766 }
1767 else
1768 {
1769 if (lzma_alone_decoder(&lzma->stream, memlimit) != LZMA_OK)
1770 return false;
1771 }
1772 lzma->compressing = false;
1773 }
1774 return true;
1775 }
f63123c3 1776 virtual ssize_t InternalUnbufferedRead(void * const To, unsigned long long const Size) override
fa89055f
DK
1777 {
1778 ssize_t Res;
1779 if (lzma->eof == true)
1780 return 0;
1781
1782 lzma->stream.next_out = (uint8_t *) To;
1783 lzma->stream.avail_out = Size;
1784 if (lzma->stream.avail_in == 0)
1785 {
1786 lzma->stream.next_in = lzma->buffer;
1787 lzma->stream.avail_in = fread(lzma->buffer, 1, sizeof(lzma->buffer)/sizeof(lzma->buffer[0]), lzma->file);
1788 }
1789 lzma->err = lzma_code(&lzma->stream, LZMA_RUN);
1790 if (lzma->err == LZMA_STREAM_END)
1791 {
1792 lzma->eof = true;
1793 Res = Size - lzma->stream.avail_out;
1794 }
1795 else if (lzma->err != LZMA_OK)
1796 {
1797 Res = -1;
1798 errno = 0;
1799 }
1800 else
1801 {
1802 Res = Size - lzma->stream.avail_out;
1803 if (Res == 0)
1804 {
1805 // lzma run was okay, but produced no output…
1806 Res = -1;
1807 errno = EINTR;
1808 }
1809 }
1810 return Res;
1811 }
1812 virtual bool InternalReadError() override
1813 {
1814 return filefd->FileFdError("lzma_read: %s (%d)", _("Read error"), lzma->err);
1815 }
1816 virtual ssize_t InternalWrite(void const * const From, unsigned long long const Size) override
1817 {
1818 lzma->stream.next_in = (uint8_t *)From;
1819 lzma->stream.avail_in = Size;
1820 lzma->stream.next_out = lzma->buffer;
1821 lzma->stream.avail_out = sizeof(lzma->buffer)/sizeof(lzma->buffer[0]);
1822 lzma->err = lzma_code(&lzma->stream, LZMA_RUN);
1823 if (lzma->err != LZMA_OK)
1824 return -1;
1825 size_t const n = sizeof(lzma->buffer)/sizeof(lzma->buffer[0]) - lzma->stream.avail_out;
1826 size_t const m = (n == 0) ? 0 : fwrite(lzma->buffer, 1, n, lzma->file);
1827 if (m != n)
1828 return -1;
1829 else
1830 return Size - lzma->stream.avail_in;
1831 }
1832 virtual bool InternalWriteError() override
1833 {
1834 return filefd->FileFdError("lzma_write: %s (%d)", _("Write error"), lzma->err);
1835 }
1836 virtual bool InternalStream() const override { return true; }
1837 virtual bool InternalClose(std::string const &) override
1838 {
1839 delete lzma;
1840 lzma = nullptr;
1841 return true;
1842 }
1843
11755147 1844 explicit LzmaFileFdPrivate(FileFd * const filefd) : FileFdPrivate(filefd), lzma(nullptr) {}
fa89055f 1845 virtual ~LzmaFileFdPrivate() { InternalClose(""); }
4239dbca 1846#endif
fa89055f
DK
1847};
1848 /*}}}*/
65ac6aad 1849class APT_HIDDEN PipedFileFdPrivate: public FileFdPrivate /*{{{*/
fa89055f
DK
1850/* if we don't have a specific class dealing with library calls, we (un)compress
1851 by executing a specified binary and pipe in/out what we need */
1852{
1853public:
1854 virtual bool InternalOpen(int const, unsigned int const Mode) override
1855 {
1856 // collect zombies here in case we reopen
1857 if (compressor_pid > 0)
1858 ExecWait(compressor_pid, "FileFdCompressor", true);
1859
1860 if ((Mode & FileFd::ReadWrite) == FileFd::ReadWrite)
1861 return filefd->FileFdError("ReadWrite mode is not supported for file %s", filefd->FileName.c_str());
4239dbca 1862
fa89055f
DK
1863 bool const Comp = (Mode & FileFd::WriteOnly) == FileFd::WriteOnly;
1864 if (Comp == false)
1865 {
1866 // Handle 'decompression' of empty files
1867 struct stat Buf;
1868 fstat(filefd->iFd, &Buf);
1869 if (Buf.st_size == 0 && S_ISFIFO(Buf.st_mode) == false)
1870 return true;
1871
1872 // We don't need the file open - instead let the compressor open it
1873 // as he properly knows better how to efficiently read from 'his' file
1874 if (filefd->FileName.empty() == false)
1875 {
1876 close(filefd->iFd);
1877 filefd->iFd = -1;
1878 }
1879 }
1880
1881 // Create a data pipe
1882 int Pipe[2] = {-1,-1};
1883 if (pipe(Pipe) != 0)
1884 return filefd->FileFdErrno("pipe",_("Failed to create subprocess IPC"));
1885 for (int J = 0; J != 2; J++)
1886 SetCloseExec(Pipe[J],true);
1887
1888 compressed_fd = filefd->iFd;
1d68256d 1889 set_is_pipe(true);
fa89055f
DK
1890
1891 if (Comp == true)
1892 filefd->iFd = Pipe[1];
1893 else
1894 filefd->iFd = Pipe[0];
1895
1896 // The child..
1897 compressor_pid = ExecFork();
1898 if (compressor_pid == 0)
1899 {
1900 if (Comp == true)
1901 {
1902 dup2(compressed_fd,STDOUT_FILENO);
1903 dup2(Pipe[0],STDIN_FILENO);
1904 }
1905 else
1906 {
1907 if (compressed_fd != -1)
1908 dup2(compressed_fd,STDIN_FILENO);
1909 dup2(Pipe[1],STDOUT_FILENO);
1910 }
1911 int const nullfd = open("/dev/null", O_WRONLY);
1912 if (nullfd != -1)
1913 {
1914 dup2(nullfd,STDERR_FILENO);
1915 close(nullfd);
1916 }
1917
1918 SetCloseExec(STDOUT_FILENO,false);
1919 SetCloseExec(STDIN_FILENO,false);
1920
1921 std::vector<char const*> Args;
1922 Args.push_back(compressor.Binary.c_str());
1923 std::vector<std::string> const * const addArgs =
1924 (Comp == true) ? &(compressor.CompressArgs) : &(compressor.UncompressArgs);
1925 for (std::vector<std::string>::const_iterator a = addArgs->begin();
1926 a != addArgs->end(); ++a)
1927 Args.push_back(a->c_str());
1928 if (Comp == false && filefd->FileName.empty() == false)
1929 {
1930 // commands not needing arguments, do not need to be told about using standard output
1931 // in reality, only testcases with tools like cat, rev, rot13, … are able to trigger this
1932 if (compressor.CompressArgs.empty() == false && compressor.UncompressArgs.empty() == false)
1933 Args.push_back("--stdout");
1934 if (filefd->TemporaryFileName.empty() == false)
1935 Args.push_back(filefd->TemporaryFileName.c_str());
1936 else
1937 Args.push_back(filefd->FileName.c_str());
1938 }
1939 Args.push_back(NULL);
1940
1941 execvp(Args[0],(char **)&Args[0]);
1942 cerr << _("Failed to exec compressor ") << Args[0] << endl;
1943 _exit(100);
1944 }
1945 if (Comp == true)
1946 close(Pipe[0]);
1947 else
1948 close(Pipe[1]);
1949
1950 return true;
1951 }
f63123c3 1952 virtual ssize_t InternalUnbufferedRead(void * const To, unsigned long long const Size) override
fa89055f
DK
1953 {
1954 return read(filefd->iFd, To, Size);
1955 }
1956 virtual ssize_t InternalWrite(void const * const From, unsigned long long const Size) override
1957 {
1958 return write(filefd->iFd, From, Size);
1959 }
1960 virtual bool InternalClose(std::string const &) override
1961 {
1962 bool Ret = true;
1963 if (compressor_pid > 0)
1964 Ret &= ExecWait(compressor_pid, "FileFdCompressor", true);
1965 compressor_pid = -1;
1966 return Ret;
1967 }
11755147 1968 explicit PipedFileFdPrivate(FileFd * const filefd) : FileFdPrivate(filefd) {}
fa89055f
DK
1969 virtual ~PipedFileFdPrivate() { InternalClose(""); }
1970};
1971 /*}}}*/
65ac6aad 1972class APT_HIDDEN DirectFileFdPrivate: public FileFdPrivate /*{{{*/
fa89055f
DK
1973{
1974public:
1975 virtual bool InternalOpen(int const, unsigned int const) override { return true; }
f63123c3 1976 virtual ssize_t InternalUnbufferedRead(void * const To, unsigned long long const Size) override
fa89055f
DK
1977 {
1978 return read(filefd->iFd, To, Size);
1979 }
fa89055f
DK
1980 virtual ssize_t InternalWrite(void const * const From, unsigned long long const Size) override
1981 {
f63123c3 1982 // files opened read+write are strange and only really "supported" for direct files
83e22e26 1983 if (buffer.size() != 0)
f63123c3 1984 {
83e22e26
JAK
1985 lseek(filefd->iFd, -buffer.size(), SEEK_CUR);
1986 buffer.reset();
f63123c3 1987 }
fa89055f
DK
1988 return write(filefd->iFd, From, Size);
1989 }
1990 virtual bool InternalSeek(unsigned long long const To) override
1991 {
1992 off_t const res = lseek(filefd->iFd, To, SEEK_SET);
1993 if (res != (off_t)To)
1994 return filefd->FileFdError("Unable to seek to %llu", To);
1995 seekpos = To;
83e22e26 1996 buffer.reset();
fa89055f
DK
1997 return true;
1998 }
1999 virtual bool InternalSkip(unsigned long long Over) override
2000 {
83e22e26 2001 if (Over >= buffer.size())
f63123c3 2002 {
83e22e26
JAK
2003 Over -= buffer.size();
2004 buffer.reset();
f63123c3
DK
2005 }
2006 else
2007 {
83e22e26 2008 buffer.bufferstart += Over;
f63123c3
DK
2009 return true;
2010 }
2011 if (Over == 0)
2012 return true;
fa89055f
DK
2013 off_t const res = lseek(filefd->iFd, Over, SEEK_CUR);
2014 if (res < 0)
2015 return filefd->FileFdError("Unable to seek ahead %llu",Over);
2016 seekpos = res;
2017 return true;
2018 }
2019 virtual bool InternalTruncate(unsigned long long const To) override
2020 {
83e22e26 2021 if (buffer.size() != 0)
f63123c3
DK
2022 {
2023 unsigned long long const seekpos = lseek(filefd->iFd, 0, SEEK_CUR);
83e22e26
JAK
2024 if ((seekpos - buffer.size()) >= To)
2025 buffer.reset();
f63123c3 2026 else if (seekpos >= To)
83e22e26 2027 buffer.bufferend = (To - seekpos) + buffer.bufferstart;
f63123c3 2028 else
83e22e26 2029 buffer.reset();
f63123c3 2030 }
fa89055f
DK
2031 if (ftruncate(filefd->iFd, To) != 0)
2032 return filefd->FileFdError("Unable to truncate to %llu",To);
2033 return true;
2034 }
2035 virtual unsigned long long InternalTell() override
2036 {
83e22e26 2037 return lseek(filefd->iFd,0,SEEK_CUR) - buffer.size();
fa89055f
DK
2038 }
2039 virtual unsigned long long InternalSize() override
2040 {
2041 return filefd->FileSize();
2042 }
2043 virtual bool InternalClose(std::string const &) override { return true; }
2044 virtual bool InternalAlwaysAutoClose() const override { return false; }
2045
11755147 2046 explicit DirectFileFdPrivate(FileFd * const filefd) : FileFdPrivate(filefd) {}
fa89055f 2047 virtual ~DirectFileFdPrivate() { InternalClose(""); }
4239dbca
DK
2048};
2049 /*}}}*/
6c55f07a
DK
2050// FileFd Constructors /*{{{*/
2051FileFd::FileFd(std::string FileName,unsigned int const Mode,unsigned long AccessMode) : iFd(-1), Flags(0), d(NULL)
2052{
2053 Open(FileName,Mode, None, AccessMode);
2054}
2055FileFd::FileFd(std::string FileName,unsigned int const Mode, CompressMode Compress, unsigned long AccessMode) : iFd(-1), Flags(0), d(NULL)
2056{
2057 Open(FileName,Mode, Compress, AccessMode);
2058}
2059FileFd::FileFd() : iFd(-1), Flags(AutoClose), d(NULL) {}
2060FileFd::FileFd(int const Fd, unsigned int const Mode, CompressMode Compress) : iFd(-1), Flags(0), d(NULL)
2061{
2062 OpenDescriptor(Fd, Mode, Compress);
2063}
2064FileFd::FileFd(int const Fd, bool const AutoClose) : iFd(-1), Flags(0), d(NULL)
2065{
2066 OpenDescriptor(Fd, ReadWrite, None, AutoClose);
2067}
2068 /*}}}*/
13d87e2e 2069// FileFd::Open - Open a file /*{{{*/
578bfd0a
AL
2070// ---------------------------------------------------------------------
2071/* The most commonly used open mode combinations are given with Mode */
e5f3f8c1 2072bool FileFd::Open(string FileName,unsigned int const Mode,CompressMode Compress, unsigned long const AccessMode)
578bfd0a 2073{
257e8d66 2074 if (Mode == ReadOnlyGzip)
e5f3f8c1 2075 return Open(FileName, ReadOnly, Gzip, AccessMode);
257e8d66 2076
468720c5 2077 if (Compress == Auto && (Mode & WriteOnly) == WriteOnly)
ae635e3c 2078 return FileFdError("Autodetection on %s only works in ReadOnly openmode!", FileName.c_str());
257e8d66 2079
468720c5
DK
2080 std::vector<APT::Configuration::Compressor> const compressors = APT::Configuration::getCompressors();
2081 std::vector<APT::Configuration::Compressor>::const_iterator compressor = compressors.begin();
2082 if (Compress == Auto)
2083 {
468720c5
DK
2084 for (; compressor != compressors.end(); ++compressor)
2085 {
e788a834 2086 std::string file = FileName + compressor->Extension;
468720c5
DK
2087 if (FileExists(file) == false)
2088 continue;
2089 FileName = file;
468720c5
DK
2090 break;
2091 }
2092 }
2093 else if (Compress == Extension)
2094 {
52b47296
DK
2095 std::string::size_type const found = FileName.find_last_of('.');
2096 std::string ext;
2097 if (found != std::string::npos)
2098 {
2099 ext = FileName.substr(found);
2100 if (ext == ".new" || ext == ".bak")
2101 {
2102 std::string::size_type const found2 = FileName.find_last_of('.', found - 1);
2103 if (found2 != std::string::npos)
2104 ext = FileName.substr(found2, found - found2);
2105 else
2106 ext.clear();
2107 }
2108 }
aee1aac6
DK
2109 for (; compressor != compressors.end(); ++compressor)
2110 if (ext == compressor->Extension)
2111 break;
2112 // no matching extension - assume uncompressed (imagine files like 'example.org_Packages')
2113 if (compressor == compressors.end())
2114 for (compressor = compressors.begin(); compressor != compressors.end(); ++compressor)
2115 if (compressor->Name == ".")
468720c5 2116 break;
468720c5 2117 }
aee1aac6 2118 else
468720c5
DK
2119 {
2120 std::string name;
2121 switch (Compress)
2122 {
aee1aac6 2123 case None: name = "."; break;
468720c5
DK
2124 case Gzip: name = "gzip"; break;
2125 case Bzip2: name = "bzip2"; break;
2126 case Lzma: name = "lzma"; break;
2127 case Xz: name = "xz"; break;
e3fbd54c 2128 case Lz4: name = "lz4"; break;
aee1aac6
DK
2129 case Auto:
2130 case Extension:
52b47296 2131 // Unreachable
ae635e3c 2132 return FileFdError("Opening File %s in None, Auto or Extension should be already handled?!?", FileName.c_str());
468720c5
DK
2133 }
2134 for (; compressor != compressors.end(); ++compressor)
2135 if (compressor->Name == name)
2136 break;
aee1aac6 2137 if (compressor == compressors.end())
ae635e3c 2138 return FileFdError("Can't find a configured compressor %s for file %s", name.c_str(), FileName.c_str());
468720c5
DK
2139 }
2140
aee1aac6 2141 if (compressor == compressors.end())
ae635e3c 2142 return FileFdError("Can't find a match for specified compressor mode for file %s", FileName.c_str());
e5f3f8c1 2143 return Open(FileName, Mode, *compressor, AccessMode);
aee1aac6 2144}
e5f3f8c1 2145bool FileFd::Open(string FileName,unsigned int const Mode,APT::Configuration::Compressor const &compressor, unsigned long const AccessMode)
aee1aac6
DK
2146{
2147 Close();
aee1aac6
DK
2148 Flags = AutoClose;
2149
2150 if ((Mode & WriteOnly) != WriteOnly && (Mode & (Atomic | Create | Empty | Exclusive)) != 0)
ae635e3c 2151 return FileFdError("ReadOnly mode for %s doesn't accept additional flags!", FileName.c_str());
aee1aac6 2152 if ((Mode & ReadWrite) == 0)
ae635e3c 2153 return FileFdError("No openmode provided in FileFd::Open for %s", FileName.c_str());
468720c5 2154
cd46d4eb
DK
2155 unsigned int OpenMode = Mode;
2156 if (FileName == "/dev/null")
2157 OpenMode = OpenMode & ~(Atomic | Exclusive | Create | Empty);
2158
2159 if ((OpenMode & Atomic) == Atomic)
257e8d66
DK
2160 {
2161 Flags |= Replace;
257e8d66 2162 }
cd46d4eb 2163 else if ((OpenMode & (Exclusive | Create)) == (Exclusive | Create))
257e8d66
DK
2164 {
2165 // for atomic, this will be done by rename in Close()
ce1f3a2c 2166 RemoveFile("FileFd::Open", FileName);
257e8d66 2167 }
cd46d4eb 2168 if ((OpenMode & Empty) == Empty)
578bfd0a 2169 {
257e8d66
DK
2170 struct stat Buf;
2171 if (lstat(FileName.c_str(),&Buf) == 0 && S_ISLNK(Buf.st_mode))
ce1f3a2c 2172 RemoveFile("FileFd::Open", FileName);
257e8d66 2173 }
c4fc2fd7 2174
561f860a 2175 int fileflags = 0;
cd46d4eb 2176 #define if_FLAGGED_SET(FLAG, MODE) if ((OpenMode & FLAG) == FLAG) fileflags |= MODE
561f860a
DK
2177 if_FLAGGED_SET(ReadWrite, O_RDWR);
2178 else if_FLAGGED_SET(ReadOnly, O_RDONLY);
2179 else if_FLAGGED_SET(WriteOnly, O_WRONLY);
4a9db827 2180
561f860a
DK
2181 if_FLAGGED_SET(Create, O_CREAT);
2182 if_FLAGGED_SET(Empty, O_TRUNC);
2183 if_FLAGGED_SET(Exclusive, O_EXCL);
561f860a 2184 #undef if_FLAGGED_SET
52b47296 2185
cd46d4eb 2186 if ((OpenMode & Atomic) == Atomic)
7335eebe
AGM
2187 {
2188 char *name = strdup((FileName + ".XXXXXX").c_str());
2189
dc545c0b 2190 if((iFd = mkstemp(name)) == -1)
7335eebe
AGM
2191 {
2192 free(name);
98b69f9d 2193 return FileFdErrno("mkstemp", "Could not create temporary file for %s", FileName.c_str());
7335eebe
AGM
2194 }
2195
2196 TemporaryFileName = string(name);
7335eebe 2197 free(name);
dc545c0b 2198
230e69d7
DK
2199 // umask() will always set the umask and return the previous value, so
2200 // we first set the umask and then reset it to the old value
2201 mode_t const CurrentUmask = umask(0);
2202 umask(CurrentUmask);
2203 // calculate the actual file permissions (just like open/creat)
2204 mode_t const FilePermissions = (AccessMode & ~CurrentUmask);
2205
2206 if(fchmod(iFd, FilePermissions) == -1)
dc545c0b 2207 return FileFdErrno("fchmod", "Could not change permissions for temporary file %s", TemporaryFileName.c_str());
7335eebe 2208 }
468720c5 2209 else
230e69d7 2210 iFd = open(FileName.c_str(), fileflags, AccessMode);
468720c5 2211
b711c01e 2212 this->FileName = FileName;
cd46d4eb 2213 if (iFd == -1 || OpenInternDescriptor(OpenMode, compressor) == false)
561f860a 2214 {
468720c5 2215 if (iFd != -1)
fc81e8f2 2216 {
561f860a
DK
2217 close (iFd);
2218 iFd = -1;
fc81e8f2 2219 }
ae635e3c 2220 return FileFdErrno("open",_("Could not open file %s"), FileName.c_str());
257e8d66 2221 }
578bfd0a 2222
13d87e2e
AL
2223 SetCloseExec(iFd,true);
2224 return true;
578bfd0a 2225}
257e8d66
DK
2226 /*}}}*/
2227// FileFd::OpenDescriptor - Open a filedescriptor /*{{{*/
52b47296 2228bool FileFd::OpenDescriptor(int Fd, unsigned int const Mode, CompressMode Compress, bool AutoClose)
aee1aac6
DK
2229{
2230 std::vector<APT::Configuration::Compressor> const compressors = APT::Configuration::getCompressors();
2231 std::vector<APT::Configuration::Compressor>::const_iterator compressor = compressors.begin();
2232 std::string name;
bce778a3
MV
2233
2234 // compat with the old API
2235 if (Mode == ReadOnlyGzip && Compress == None)
2236 Compress = Gzip;
2237
aee1aac6
DK
2238 switch (Compress)
2239 {
2240 case None: name = "."; break;
2241 case Gzip: name = "gzip"; break;
2242 case Bzip2: name = "bzip2"; break;
2243 case Lzma: name = "lzma"; break;
2244 case Xz: name = "xz"; break;
e3fbd54c 2245 case Lz4: name = "lz4"; break;
aee1aac6
DK
2246 case Auto:
2247 case Extension:
f97bb523
DK
2248 if (AutoClose == true && Fd != -1)
2249 close(Fd);
ae635e3c 2250 return FileFdError("Opening Fd %d in Auto or Extension compression mode is not supported", Fd);
aee1aac6
DK
2251 }
2252 for (; compressor != compressors.end(); ++compressor)
2253 if (compressor->Name == name)
2254 break;
2255 if (compressor == compressors.end())
f97bb523
DK
2256 {
2257 if (AutoClose == true && Fd != -1)
2258 close(Fd);
ae635e3c 2259 return FileFdError("Can't find a configured compressor %s for file %s", name.c_str(), FileName.c_str());
f97bb523 2260 }
aee1aac6
DK
2261 return OpenDescriptor(Fd, Mode, *compressor, AutoClose);
2262}
52b47296 2263bool FileFd::OpenDescriptor(int Fd, unsigned int const Mode, APT::Configuration::Compressor const &compressor, bool AutoClose)
144c0969
JAK
2264{
2265 Close();
2266 Flags = (AutoClose) ? FileFd::AutoClose : 0;
84baaae9 2267 iFd = Fd;
b711c01e 2268 this->FileName = "";
84baaae9 2269 if (OpenInternDescriptor(Mode, compressor) == false)
468720c5 2270 {
f97bb523 2271 if (iFd != -1 && (
84baaae9 2272 (Flags & Compressed) == Compressed ||
f97bb523
DK
2273 AutoClose == true))
2274 {
468720c5 2275 close (iFd);
f97bb523
DK
2276 iFd = -1;
2277 }
2278 return FileFdError(_("Could not open file descriptor %d"), Fd);
144c0969 2279 }
144c0969 2280 return true;
468720c5 2281}
52b47296 2282bool FileFd::OpenInternDescriptor(unsigned int const Mode, APT::Configuration::Compressor const &compressor)
468720c5 2283{
84baaae9
DK
2284 if (iFd == -1)
2285 return false;
ff477ee1 2286
fa89055f
DK
2287 if (d != nullptr)
2288 d->InternalClose(FileName);
2289
2290 if (d == nullptr)
2291 {
2292 if (false)
2293 /* dummy so that the rest can be 'else if's */;
2294#define APT_COMPRESS_INIT(NAME, CONSTRUCTOR) \
2295 else if (compressor.Name == NAME) \
2296 d = new CONSTRUCTOR(this)
69d6988a 2297#ifdef HAVE_ZLIB
fa89055f 2298 APT_COMPRESS_INIT("gzip", GzipFileFdPrivate);
69d6988a
DK
2299#endif
2300#ifdef HAVE_BZ2
fa89055f 2301 APT_COMPRESS_INIT("bzip2", Bz2FileFdPrivate);
69d6988a 2302#endif
7f350a37 2303#ifdef HAVE_LZMA
fa89055f
DK
2304 APT_COMPRESS_INIT("xz", LzmaFileFdPrivate);
2305 APT_COMPRESS_INIT("lzma", LzmaFileFdPrivate);
7f350a37 2306#endif
e3fbd54c
JAK
2307#ifdef HAVE_LZ4
2308 APT_COMPRESS_INIT("lz4", Lz4FileFdPrivate);
2309#endif
69d6988a 2310#undef APT_COMPRESS_INIT
fa89055f
DK
2311 else if (compressor.Name == "." || compressor.Binary.empty() == true)
2312 d = new DirectFileFdPrivate(this);
2313 else
2314 d = new PipedFileFdPrivate(this);
69d6988a 2315
88749b5d
JAK
2316 if (Mode & BufferedWrite)
2317 d = new BufferedWriteFileFdPrivate(d);
2318
1d68256d
JAK
2319 d->set_openmode(Mode);
2320 d->set_compressor(compressor);
fa89055f 2321 if ((Flags & AutoClose) != AutoClose && d->InternalAlwaysAutoClose())
84baaae9
DK
2322 {
2323 // Need to duplicate fd here or gz/bz2 close for cleanup will close the fd as well
2324 int const internFd = dup(iFd);
2325 if (internFd == -1)
2326 return FileFdErrno("OpenInternDescriptor", _("Could not open file descriptor %d"), iFd);
2327 iFd = internFd;
2328 }
561f860a 2329 }
fa89055f 2330 return d->InternalOpen(iFd, Mode);
144c0969 2331}
578bfd0a 2332 /*}}}*/
8e06abb2 2333// FileFd::~File - Closes the file /*{{{*/
578bfd0a
AL
2334// ---------------------------------------------------------------------
2335/* If the proper modes are selected then we close the Fd and possibly
2336 unlink the file on error. */
8e06abb2 2337FileFd::~FileFd()
578bfd0a
AL
2338{
2339 Close();
500400fe 2340 if (d != NULL)
fa89055f 2341 d->InternalClose(FileName);
96ab3c6f
MV
2342 delete d;
2343 d = NULL;
578bfd0a
AL
2344}
2345 /*}}}*/
8e06abb2 2346// FileFd::Read - Read a bit of the file /*{{{*/
578bfd0a 2347// ---------------------------------------------------------------------
1e3f4083 2348/* We are careful to handle interruption by a signal while reading
b0db36b1 2349 gracefully. */
650faab0 2350bool FileFd::Read(void *To,unsigned long long Size,unsigned long long *Actual)
578bfd0a 2351{
fa89055f
DK
2352 if (d == nullptr)
2353 return false;
39e77e45 2354 ssize_t Res = 1;
b0db36b1 2355 errno = 0;
f604cf55
AL
2356 if (Actual != 0)
2357 *Actual = 0;
699b209e 2358 *((char *)To) = '\0';
39e77e45 2359 while (Res > 0 && Size > 0)
578bfd0a 2360 {
fa89055f 2361 Res = d->InternalRead(To, Size);
b711c01e 2362
b0db36b1
AL
2363 if (Res < 0)
2364 {
b711c01e 2365 if (errno == EINTR)
c4b113e6
DK
2366 {
2367 // trick the while-loop into running again
2368 Res = 1;
2369 errno = 0;
b711c01e 2370 continue;
c4b113e6 2371 }
fa89055f 2372 return d->InternalReadError();
b0db36b1 2373 }
578bfd0a 2374
b0db36b1
AL
2375 To = (char *)To + Res;
2376 Size -= Res;
ff477ee1 2377 if (d != NULL)
1d68256d 2378 d->set_seekpos(d->get_seekpos() + Res);
f604cf55
AL
2379 if (Actual != 0)
2380 *Actual += Res;
b0db36b1 2381 }
b0db36b1
AL
2382
2383 if (Size == 0)
2384 return true;
2385
ddc1d8d0 2386 // Eof handling
f604cf55 2387 if (Actual != 0)
ddc1d8d0
AL
2388 {
2389 Flags |= HitEof;
2390 return true;
2391 }
ae635e3c
DK
2392
2393 return FileFdError(_("read, still have %llu to read but none left"), Size);
578bfd0a
AL
2394}
2395 /*}}}*/
032bd56f
DK
2396// FileFd::ReadLine - Read a complete line from the file /*{{{*/
2397// ---------------------------------------------------------------------
fa89055f 2398/* Beware: This method can be quite slow for big buffers on UNcompressed
032bd56f
DK
2399 files because of the naive implementation! */
2400char* FileFd::ReadLine(char *To, unsigned long long const Size)
2401{
699b209e 2402 *To = '\0';
fa89055f
DK
2403 if (d == nullptr)
2404 return nullptr;
2405 return d->InternalReadLine(To, Size);
032bd56f
DK
2406}
2407 /*}}}*/
766761fd
JAK
2408// FileFd::Flush - Flush the file /*{{{*/
2409bool FileFd::Flush()
2410{
2411 if (d == nullptr)
2412 return true;
2413
2414 return d->InternalFlush();
2415}
2416 /*}}}*/
8e06abb2 2417// FileFd::Write - Write to the file /*{{{*/
650faab0 2418bool FileFd::Write(const void *From,unsigned long long Size)
578bfd0a 2419{
fa89055f
DK
2420 if (d == nullptr)
2421 return false;
5df91bc7 2422 ssize_t Res = 1;
b0db36b1 2423 errno = 0;
5df91bc7 2424 while (Res > 0 && Size > 0)
578bfd0a 2425 {
fa89055f 2426 Res = d->InternalWrite(From, Size);
b0db36b1
AL
2427 if (Res < 0 && errno == EINTR)
2428 continue;
2429 if (Res < 0)
fa89055f
DK
2430 return d->InternalWriteError();
2431
cf4ff3b7 2432 From = (char const *)From + Res;
b0db36b1 2433 Size -= Res;
ff477ee1 2434 if (d != NULL)
1d68256d 2435 d->set_seekpos(d->get_seekpos() + Res);
578bfd0a 2436 }
fa89055f 2437
b0db36b1
AL
2438 if (Size == 0)
2439 return true;
ae635e3c
DK
2440
2441 return FileFdError(_("write, still have %llu to write but couldn't"), Size);
d68d65ad
DK
2442}
2443bool FileFd::Write(int Fd, const void *From, unsigned long long Size)
2444{
5df91bc7 2445 ssize_t Res = 1;
d68d65ad 2446 errno = 0;
5df91bc7 2447 while (Res > 0 && Size > 0)
d68d65ad
DK
2448 {
2449 Res = write(Fd,From,Size);
2450 if (Res < 0 && errno == EINTR)
2451 continue;
2452 if (Res < 0)
2453 return _error->Errno("write",_("Write error"));
2454
cf4ff3b7 2455 From = (char const *)From + Res;
d68d65ad
DK
2456 Size -= Res;
2457 }
d68d65ad
DK
2458
2459 if (Size == 0)
2460 return true;
2461
2462 return _error->Error(_("write, still have %llu to write but couldn't"), Size);
578bfd0a
AL
2463}
2464 /*}}}*/
8e06abb2 2465// FileFd::Seek - Seek in the file /*{{{*/
650faab0 2466bool FileFd::Seek(unsigned long long To)
578bfd0a 2467{
fa89055f
DK
2468 if (d == nullptr)
2469 return false;
bb93178b 2470 Flags &= ~HitEof;
fa89055f 2471 return d->InternalSeek(To);
727f18af
AL
2472}
2473 /*}}}*/
fa89055f 2474// FileFd::Skip - Skip over data in the file /*{{{*/
650faab0 2475bool FileFd::Skip(unsigned long long Over)
727f18af 2476{
fa89055f
DK
2477 if (d == nullptr)
2478 return false;
2479 return d->InternalSkip(Over);
6d5dd02a
AL
2480}
2481 /*}}}*/
fa89055f 2482// FileFd::Truncate - Truncate the file /*{{{*/
650faab0 2483bool FileFd::Truncate(unsigned long long To)
6d5dd02a 2484{
fa89055f
DK
2485 if (d == nullptr)
2486 return false;
ad5051ef
DK
2487 // truncating /dev/null is always successful - as we get an error otherwise
2488 if (To == 0 && FileName == "/dev/null")
2489 return true;
fa89055f 2490 return d->InternalTruncate(To);
578bfd0a
AL
2491}
2492 /*}}}*/
7f25bdff
AL
2493// FileFd::Tell - Current seek position /*{{{*/
2494// ---------------------------------------------------------------------
2495/* */
650faab0 2496unsigned long long FileFd::Tell()
7f25bdff 2497{
fa89055f
DK
2498 if (d == nullptr)
2499 return false;
2500 off_t const Res = d->InternalTell();
7f25bdff 2501 if (Res == (off_t)-1)
ae635e3c 2502 FileFdErrno("lseek","Failed to determine the current file position");
1d68256d 2503 d->set_seekpos(Res);
7f25bdff
AL
2504 return Res;
2505}
2506 /*}}}*/
8190b07a 2507static bool StatFileFd(char const * const msg, int const iFd, std::string const &FileName, struct stat &Buf, FileFdPrivate * const d) /*{{{*/
578bfd0a 2508{
1d68256d 2509 bool ispipe = (d != NULL && d->get_is_pipe() == true);
6008b79a
DK
2510 if (ispipe == false)
2511 {
2512 if (fstat(iFd,&Buf) != 0)
8190b07a
DK
2513 // higher-level code will generate more meaningful messages,
2514 // even translated this would be meaningless for users
2515 return _error->Errno("fstat", "Unable to determine %s for fd %i", msg, iFd);
003c40d3
DK
2516 if (FileName.empty() == false)
2517 ispipe = S_ISFIFO(Buf.st_mode);
6008b79a 2518 }
699b209e
DK
2519
2520 // for compressor pipes st_size is undefined and at 'best' zero
6008b79a 2521 if (ispipe == true)
699b209e
DK
2522 {
2523 // we set it here, too, as we get the info here for free
2524 // in theory the Open-methods should take care of it already
ff477ee1 2525 if (d != NULL)
1d68256d 2526 d->set_is_pipe(true);
699b209e 2527 if (stat(FileName.c_str(), &Buf) != 0)
8190b07a
DK
2528 return _error->Errno("fstat", "Unable to determine %s for file %s", msg, FileName.c_str());
2529 }
2530 return true;
2531}
2532 /*}}}*/
2533// FileFd::FileSize - Return the size of the file /*{{{*/
2534unsigned long long FileFd::FileSize()
2535{
2536 struct stat Buf;
2537 if (StatFileFd("file size", iFd, FileName, Buf, d) == false)
2538 {
2539 Flags |= Fail;
2540 return 0;
699b209e 2541 }
4260fd39
DK
2542 return Buf.st_size;
2543}
2544 /*}}}*/
8190b07a
DK
2545// FileFd::ModificationTime - Return the time of last touch /*{{{*/
2546time_t FileFd::ModificationTime()
2547{
2548 struct stat Buf;
2549 if (StatFileFd("modification time", iFd, FileName, Buf, d) == false)
2550 {
2551 Flags |= Fail;
2552 return 0;
2553 }
2554 return Buf.st_mtime;
2555}
2556 /*}}}*/
4260fd39 2557// FileFd::Size - Return the size of the content in the file /*{{{*/
650faab0 2558unsigned long long FileFd::Size()
4260fd39 2559{
fa89055f
DK
2560 if (d == nullptr)
2561 return false;
2562 return d->InternalSize();
578bfd0a
AL
2563}
2564 /*}}}*/
8e06abb2 2565// FileFd::Close - Close the file if the close flag is set /*{{{*/
578bfd0a
AL
2566// ---------------------------------------------------------------------
2567/* */
8e06abb2 2568bool FileFd::Close()
578bfd0a 2569{
766761fd
JAK
2570 if (Flush() == false)
2571 return false;
032bd56f
DK
2572 if (iFd == -1)
2573 return true;
2574
578bfd0a
AL
2575 bool Res = true;
2576 if ((Flags & AutoClose) == AutoClose)
d13c2d3f 2577 {
500400fe
DK
2578 if ((Flags & Compressed) != Compressed && iFd > 0 && close(iFd) != 0)
2579 Res &= _error->Errno("close",_("Problem closing the file %s"), FileName.c_str());
2da8aae5
JAK
2580 }
2581
2582 if (d != NULL)
2583 {
fa89055f 2584 Res &= d->InternalClose(FileName);
2da8aae5
JAK
2585 delete d;
2586 d = NULL;
d13c2d3f 2587 }
3010fb0e 2588
d3aac32e 2589 if ((Flags & Replace) == Replace) {
3010fb0e 2590 if (rename(TemporaryFileName.c_str(), FileName.c_str()) != 0)
62d073d9
DK
2591 Res &= _error->Errno("rename",_("Problem renaming the file %s to %s"), TemporaryFileName.c_str(), FileName.c_str());
2592
fd3b761e 2593 FileName = TemporaryFileName; // for the unlink() below.
257e8d66 2594 TemporaryFileName.clear();
3010fb0e 2595 }
62d073d9
DK
2596
2597 iFd = -1;
2598
578bfd0a
AL
2599 if ((Flags & Fail) == Fail && (Flags & DelOnFail) == DelOnFail &&
2600 FileName.empty() == false)
ce1f3a2c 2601 Res &= RemoveFile("FileFd::Close", FileName);
3010fb0e 2602
fbb89d94
DK
2603 if (Res == false)
2604 Flags |= Fail;
578bfd0a
AL
2605 return Res;
2606}
2607 /*}}}*/
b2e465d6
AL
2608// FileFd::Sync - Sync the file /*{{{*/
2609// ---------------------------------------------------------------------
2610/* */
2611bool FileFd::Sync()
2612{
b2e465d6 2613 if (fsync(iFd) != 0)
ae635e3c
DK
2614 return FileFdErrno("sync",_("Problem syncing the file"));
2615 return true;
2616}
2617 /*}}}*/
2618// FileFd::FileFdErrno - set Fail and call _error->Errno *{{{*/
2619bool FileFd::FileFdErrno(const char *Function, const char *Description,...)
2620{
2621 Flags |= Fail;
2622 va_list args;
2623 size_t msgSize = 400;
2624 int const errsv = errno;
2625 while (true)
fbb89d94 2626 {
ae635e3c
DK
2627 va_start(args,Description);
2628 if (_error->InsertErrno(GlobalError::ERROR, Function, Description, args, errsv, msgSize) == false)
2629 break;
2630 va_end(args);
fbb89d94 2631 }
ae635e3c
DK
2632 return false;
2633}
2634 /*}}}*/
2635// FileFd::FileFdError - set Fail and call _error->Error *{{{*/
2636bool FileFd::FileFdError(const char *Description,...) {
2637 Flags |= Fail;
2638 va_list args;
2639 size_t msgSize = 400;
2640 while (true)
2641 {
2642 va_start(args,Description);
2643 if (_error->Insert(GlobalError::ERROR, Description, args, msgSize) == false)
2644 break;
2645 va_end(args);
2646 }
2647 return false;
b2e465d6
AL
2648}
2649 /*}}}*/
fa89055f 2650gzFile FileFd::gzFd() { /*{{{*/
7f350a37 2651#ifdef HAVE_ZLIB
fa89055f
DK
2652 GzipFileFdPrivate * const gzipd = dynamic_cast<GzipFileFdPrivate*>(d);
2653 if (gzipd == nullptr)
2654 return nullptr;
2655 else
2656 return gzipd->gz;
7f350a37 2657#else
fa89055f 2658 return nullptr;
7f350a37
DK
2659#endif
2660}
fa89055f 2661 /*}}}*/
8d01b9d6 2662
f8aba23f 2663// Glob - wrapper around "glob()" /*{{{*/
8d01b9d6
MV
2664std::vector<std::string> Glob(std::string const &pattern, int flags)
2665{
2666 std::vector<std::string> result;
2667 glob_t globbuf;
ec4835a1
ÁGM
2668 int glob_res;
2669 unsigned int i;
8d01b9d6
MV
2670
2671 glob_res = glob(pattern.c_str(), flags, NULL, &globbuf);
2672
2673 if (glob_res != 0)
2674 {
2675 if(glob_res != GLOB_NOMATCH) {
2676 _error->Errno("glob", "Problem with glob");
2677 return result;
2678 }
2679 }
2680
2681 // append results
2682 for(i=0;i<globbuf.gl_pathc;i++)
2683 result.push_back(string(globbuf.gl_pathv[i]));
2684
2685 globfree(&globbuf);
2686 return result;
2687}
2688 /*}}}*/
f8aba23f 2689std::string GetTempDir() /*{{{*/
68e01721
MV
2690{
2691 const char *tmpdir = getenv("TMPDIR");
2692
2693#ifdef P_tmpdir
2694 if (!tmpdir)
2695 tmpdir = P_tmpdir;
2696#endif
2697
68e01721 2698 struct stat st;
0d303f17 2699 if (!tmpdir || strlen(tmpdir) == 0 || // tmpdir is set
dd6da7d2
DK
2700 stat(tmpdir, &st) != 0 || (st.st_mode & S_IFDIR) == 0) // exists and is directory
2701 tmpdir = "/tmp";
2702 else if (geteuid() != 0 && // root can do everything anyway
2703 faccessat(-1, tmpdir, R_OK | W_OK | X_OK, AT_EACCESS | AT_SYMLINK_NOFOLLOW) != 0) // current user has rwx access to directory
68e01721
MV
2704 tmpdir = "/tmp";
2705
2706 return string(tmpdir);
dd6da7d2
DK
2707}
2708std::string GetTempDir(std::string const &User)
2709{
2710 // no need/possibility to drop privs
2711 if(getuid() != 0 || User.empty() || User == "root")
2712 return GetTempDir();
2713
2714 struct passwd const * const pw = getpwnam(User.c_str());
2715 if (pw == NULL)
2716 return GetTempDir();
2717
226c0f64
DK
2718 gid_t const old_euid = geteuid();
2719 gid_t const old_egid = getegid();
dd6da7d2
DK
2720 if (setegid(pw->pw_gid) != 0)
2721 _error->Errno("setegid", "setegid %u failed", pw->pw_gid);
2722 if (seteuid(pw->pw_uid) != 0)
2723 _error->Errno("seteuid", "seteuid %u failed", pw->pw_uid);
2724
2725 std::string const tmp = GetTempDir();
2726
226c0f64
DK
2727 if (seteuid(old_euid) != 0)
2728 _error->Errno("seteuid", "seteuid %u failed", old_euid);
2729 if (setegid(old_egid) != 0)
2730 _error->Errno("setegid", "setegid %u failed", old_egid);
dd6da7d2
DK
2731
2732 return tmp;
68e01721 2733}
f8aba23f 2734 /*}}}*/
c9443c01 2735FileFd* GetTempFile(std::string const &Prefix, bool ImmediateUnlink, FileFd * const TmpFd) /*{{{*/
0d29b9d4
MV
2736{
2737 char fn[512];
c9443c01 2738 FileFd * const Fd = TmpFd == NULL ? new FileFd() : TmpFd;
0d29b9d4 2739
c9443c01
DK
2740 std::string const tempdir = GetTempDir();
2741 snprintf(fn, sizeof(fn), "%s/%s.XXXXXX",
0d29b9d4 2742 tempdir.c_str(), Prefix.c_str());
c9443c01 2743 int const fd = mkstemp(fn);
0d29b9d4
MV
2744 if(ImmediateUnlink)
2745 unlink(fn);
c9443c01 2746 if (fd < 0)
0d29b9d4
MV
2747 {
2748 _error->Errno("GetTempFile",_("Unable to mkstemp %s"), fn);
2749 return NULL;
2750 }
c9443c01 2751 if (!Fd->OpenDescriptor(fd, FileFd::ReadWrite, FileFd::None, true))
0d29b9d4
MV
2752 {
2753 _error->Errno("GetTempFile",_("Unable to write to %s"),fn);
2754 return NULL;
2755 }
0d29b9d4
MV
2756 return Fd;
2757}
f8aba23f
DK
2758 /*}}}*/
2759bool Rename(std::string From, std::string To) /*{{{*/
c1409d1b
MV
2760{
2761 if (rename(From.c_str(),To.c_str()) != 0)
2762 {
2763 _error->Error(_("rename failed, %s (%s -> %s)."),strerror(errno),
2764 From.c_str(),To.c_str());
2765 return false;
f8aba23f 2766 }
c1409d1b
MV
2767 return true;
2768}
f8aba23f
DK
2769 /*}}}*/
2770bool Popen(const char* Args[], FileFd &Fd, pid_t &Child, FileFd::OpenMode Mode)/*{{{*/
7ad2a347
MV
2771{
2772 int fd;
2773 if (Mode != FileFd::ReadOnly && Mode != FileFd::WriteOnly)
2774 return _error->Error("Popen supports ReadOnly (x)or WriteOnly mode only");
2775
2776 int Pipe[2] = {-1, -1};
2777 if(pipe(Pipe) != 0)
7ad2a347 2778 return _error->Errno("pipe", _("Failed to create subprocess IPC"));
5e49cbb7 2779
7ad2a347
MV
2780 std::set<int> keep_fds;
2781 keep_fds.insert(Pipe[0]);
2782 keep_fds.insert(Pipe[1]);
2783 Child = ExecFork(keep_fds);
2784 if(Child < 0)
2785 return _error->Errno("fork", "Failed to fork");
2786 if(Child == 0)
2787 {
2788 if(Mode == FileFd::ReadOnly)
2789 {
2790 close(Pipe[0]);
2791 fd = Pipe[1];
2792 }
2793 else if(Mode == FileFd::WriteOnly)
2794 {
2795 close(Pipe[1]);
2796 fd = Pipe[0];
2797 }
2798
2799 if(Mode == FileFd::ReadOnly)
2800 {
2801 dup2(fd, 1);
2802 dup2(fd, 2);
2803 } else if(Mode == FileFd::WriteOnly)
2804 dup2(fd, 0);
2805
2806 execv(Args[0], (char**)Args);
2807 _exit(100);
2808 }
2809 if(Mode == FileFd::ReadOnly)
2810 {
2811 close(Pipe[1]);
2812 fd = Pipe[0];
8f5b67ae
DK
2813 }
2814 else if(Mode == FileFd::WriteOnly)
7ad2a347
MV
2815 {
2816 close(Pipe[0]);
2817 fd = Pipe[1];
2818 }
8f5b67ae
DK
2819 else
2820 return _error->Error("Popen supports ReadOnly (x)or WriteOnly mode only");
7ad2a347
MV
2821 Fd.OpenDescriptor(fd, Mode, FileFd::None, true);
2822
2823 return true;
2824}
f8aba23f
DK
2825 /*}}}*/
2826bool DropPrivileges() /*{{{*/
fc1a78d8 2827{
8f45798d
DK
2828 if(_config->FindB("Debug::NoDropPrivs", false) == true)
2829 return true;
2830
2831#if __gnu_linux__
2832#if defined(PR_SET_NO_NEW_PRIVS) && ( PR_SET_NO_NEW_PRIVS != 38 )
2833#error "PR_SET_NO_NEW_PRIVS is defined, but with a different value than expected!"
2834#endif
2835 // see prctl(2), needs linux3.5 at runtime - magic constant to avoid it at buildtime
2836 int ret = prctl(38, 1, 0, 0, 0);
2837 // ignore EINVAL - kernel is too old to understand the option
2838 if(ret < 0 && errno != EINVAL)
2839 _error->Warning("PR_SET_NO_NEW_PRIVS failed with %i", ret);
2840#endif
2841
990dd78a
DK
2842 // empty setting disables privilege dropping - this also ensures
2843 // backward compatibility, see bug #764506
2844 const std::string toUser = _config->Find("APT::Sandbox::User");
514a25cb 2845 if (toUser.empty() || toUser == "root")
990dd78a
DK
2846 return true;
2847
ebca2f25
DK
2848 // a lot can go wrong trying to drop privileges completely,
2849 // so ideally we would like to verify that we have done it –
2850 // but the verify asks for too much in case of fakeroot (and alike)
2851 // [Specific checks can be overridden with dedicated options]
2852 bool const VerifySandboxing = _config->FindB("APT::Sandbox::Verify", false);
2853
f1e3c8f0 2854 // uid will be 0 in the end, but gid might be different anyway
8f45798d
DK
2855 uid_t const old_uid = getuid();
2856 gid_t const old_gid = getgid();
fc1a78d8 2857
5f2047ec
JAK
2858 if (old_uid != 0)
2859 return true;
3927c6da 2860
b8dae9a1 2861 struct passwd *pw = getpwnam(toUser.c_str());
fc1a78d8 2862 if (pw == NULL)
b8dae9a1 2863 return _error->Error("No user %s, can not drop rights", toUser.c_str());
3927c6da 2864
f1e3c8f0 2865 // Do not change the order here, it might break things
5a326439 2866 // Get rid of all our supplementary groups first
3b084f06 2867 if (setgroups(1, &pw->pw_gid))
3927c6da
MV
2868 return _error->Errno("setgroups", "Failed to setgroups");
2869
5a326439
JAK
2870 // Now change the group ids to the new user
2871#ifdef HAVE_SETRESGID
2872 if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) != 0)
2873 return _error->Errno("setresgid", "Failed to set new group ids");
2874#else
3927c6da 2875 if (setegid(pw->pw_gid) != 0)
5f2047ec
JAK
2876 return _error->Errno("setegid", "Failed to setegid");
2877
fc1a78d8
MV
2878 if (setgid(pw->pw_gid) != 0)
2879 return _error->Errno("setgid", "Failed to setgid");
5a326439 2880#endif
5f2047ec 2881
5a326439
JAK
2882 // Change the user ids to the new user
2883#ifdef HAVE_SETRESUID
2884 if (setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) != 0)
2885 return _error->Errno("setresuid", "Failed to set new user ids");
2886#else
fc1a78d8
MV
2887 if (setuid(pw->pw_uid) != 0)
2888 return _error->Errno("setuid", "Failed to setuid");
5f2047ec
JAK
2889 if (seteuid(pw->pw_uid) != 0)
2890 return _error->Errno("seteuid", "Failed to seteuid");
5a326439 2891#endif
5f2047ec 2892
ebca2f25
DK
2893 // disabled by default as fakeroot doesn't implement getgroups currently (#806521)
2894 if (VerifySandboxing == true || _config->FindB("APT::Sandbox::Verify::Groups", false) == true)
2895 {
2896 // Verify that the user isn't still in any supplementary groups
2897 long const ngroups_max = sysconf(_SC_NGROUPS_MAX);
2898 std::unique_ptr<gid_t[]> gidlist(new gid_t[ngroups_max]);
2899 if (unlikely(gidlist == NULL))
2900 return _error->Error("Allocation of a list of size %lu for getgroups failed", ngroups_max);
2901 ssize_t gidlist_nr;
2902 if ((gidlist_nr = getgroups(ngroups_max, gidlist.get())) < 0)
2903 return _error->Errno("getgroups", "Could not get new groups (%lu)", ngroups_max);
2904 for (ssize_t i = 0; i < gidlist_nr; ++i)
2905 if (gidlist[i] != pw->pw_gid)
2906 return _error->Error("Could not switch group, user %s is still in group %d", toUser.c_str(), gidlist[i]);
2907 }
2908
2909 // enabled by default as all fakeroot-lookalikes should fake that accordingly
2910 if (VerifySandboxing == true || _config->FindB("APT::Sandbox::Verify::IDs", true) == true)
2911 {
2912 // Verify that gid, egid, uid, and euid changed
2913 if (getgid() != pw->pw_gid)
2914 return _error->Error("Could not switch group");
2915 if (getegid() != pw->pw_gid)
2916 return _error->Error("Could not switch effective group");
2917 if (getuid() != pw->pw_uid)
2918 return _error->Error("Could not switch user");
2919 if (geteuid() != pw->pw_uid)
2920 return _error->Error("Could not switch effective user");
5f2047ec 2921
550ab420 2922#ifdef HAVE_GETRESUID
ebca2f25
DK
2923 // verify that the saved set-user-id was changed as well
2924 uid_t ruid = 0;
2925 uid_t euid = 0;
2926 uid_t suid = 0;
2927 if (getresuid(&ruid, &euid, &suid))
2928 return _error->Errno("getresuid", "Could not get saved set-user-ID");
2929 if (suid != pw->pw_uid)
2930 return _error->Error("Could not switch saved set-user-ID");
550ab420
JAK
2931#endif
2932
2933#ifdef HAVE_GETRESGID
ebca2f25
DK
2934 // verify that the saved set-group-id was changed as well
2935 gid_t rgid = 0;
2936 gid_t egid = 0;
2937 gid_t sgid = 0;
2938 if (getresgid(&rgid, &egid, &sgid))
2939 return _error->Errno("getresuid", "Could not get saved set-group-ID");
2940 if (sgid != pw->pw_gid)
2941 return _error->Error("Could not switch saved set-group-ID");
550ab420 2942#endif
ebca2f25 2943 }
550ab420 2944
ebca2f25
DK
2945 // disabled as fakeroot doesn't forbid (by design) (re)gaining root from unprivileged
2946 if (VerifySandboxing == true || _config->FindB("APT::Sandbox::Verify::Regain", false) == true)
2947 {
2948 // Check that uid and gid changes do not work anymore
2949 if (pw->pw_gid != old_gid && (setgid(old_gid) != -1 || setegid(old_gid) != -1))
2950 return _error->Error("Could restore a gid to root, privilege dropping did not work");
bdc00df5 2951
ebca2f25
DK
2952 if (pw->pw_uid != old_uid && (setuid(old_uid) != -1 || seteuid(old_uid) != -1))
2953 return _error->Error("Could restore a uid to root, privilege dropping did not work");
2954 }
bdc00df5 2955
fc1a78d8
MV
2956 return true;
2957}
f8aba23f 2958 /*}}}*/