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