]> git.saurik.com Git - apt.git/blame - apt-pkg/contrib/fileutl.cc
Don't download "optional" files not in Release :/.
[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 }
fa89055f
DK
1959
1960 SetCloseExec(STDOUT_FILENO,false);
1961 SetCloseExec(STDIN_FILENO,false);
1962
1963 std::vector<char const*> Args;
1964 Args.push_back(compressor.Binary.c_str());
1965 std::vector<std::string> const * const addArgs =
1966 (Comp == true) ? &(compressor.CompressArgs) : &(compressor.UncompressArgs);
1967 for (std::vector<std::string>::const_iterator a = addArgs->begin();
1968 a != addArgs->end(); ++a)
1969 Args.push_back(a->c_str());
1970 if (Comp == false && filefd->FileName.empty() == false)
1971 {
1972 // commands not needing arguments, do not need to be told about using standard output
1973 // in reality, only testcases with tools like cat, rev, rot13, … are able to trigger this
1974 if (compressor.CompressArgs.empty() == false && compressor.UncompressArgs.empty() == false)
1975 Args.push_back("--stdout");
1976 if (filefd->TemporaryFileName.empty() == false)
1977 Args.push_back(filefd->TemporaryFileName.c_str());
1978 else
1979 Args.push_back(filefd->FileName.c_str());
1980 }
1981 Args.push_back(NULL);
1982
1983 execvp(Args[0],(char **)&Args[0]);
1984 cerr << _("Failed to exec compressor ") << Args[0] << endl;
1985 _exit(100);
1986 }
1987 if (Comp == true)
1988 close(Pipe[0]);
1989 else
1990 close(Pipe[1]);
1991
1992 return true;
1993 }
cabfb880 1994 virtual ssize_t InternalUnbufferedRead(void * const To, unsigned long long const Size) APT_OVERRIDE
fa89055f
DK
1995 {
1996 return read(filefd->iFd, To, Size);
1997 }
cabfb880 1998 virtual ssize_t InternalWrite(void const * const From, unsigned long long const Size) APT_OVERRIDE
fa89055f
DK
1999 {
2000 return write(filefd->iFd, From, Size);
2001 }
cabfb880 2002 virtual bool InternalClose(std::string const &) APT_OVERRIDE
fa89055f
DK
2003 {
2004 bool Ret = true;
bdc42211
DK
2005 if (filefd->iFd != -1)
2006 {
2007 close(filefd->iFd);
2008 filefd->iFd = -1;
2009 }
fa89055f
DK
2010 if (compressor_pid > 0)
2011 Ret &= ExecWait(compressor_pid, "FileFdCompressor", true);
2012 compressor_pid = -1;
2013 return Ret;
2014 }
11755147 2015 explicit PipedFileFdPrivate(FileFd * const filefd) : FileFdPrivate(filefd) {}
fa89055f
DK
2016 virtual ~PipedFileFdPrivate() { InternalClose(""); }
2017};
2018 /*}}}*/
65ac6aad 2019class APT_HIDDEN DirectFileFdPrivate: public FileFdPrivate /*{{{*/
fa89055f
DK
2020{
2021public:
cabfb880
DK
2022 virtual bool InternalOpen(int const, unsigned int const) APT_OVERRIDE { return true; }
2023 virtual ssize_t InternalUnbufferedRead(void * const To, unsigned long long const Size) APT_OVERRIDE
fa89055f
DK
2024 {
2025 return read(filefd->iFd, To, Size);
2026 }
cabfb880 2027 virtual ssize_t InternalWrite(void const * const From, unsigned long long const Size) APT_OVERRIDE
fa89055f 2028 {
f63123c3 2029 // files opened read+write are strange and only really "supported" for direct files
83e22e26 2030 if (buffer.size() != 0)
f63123c3 2031 {
83e22e26
JAK
2032 lseek(filefd->iFd, -buffer.size(), SEEK_CUR);
2033 buffer.reset();
f63123c3 2034 }
fa89055f
DK
2035 return write(filefd->iFd, From, Size);
2036 }
cabfb880 2037 virtual bool InternalSeek(unsigned long long const To) APT_OVERRIDE
fa89055f
DK
2038 {
2039 off_t const res = lseek(filefd->iFd, To, SEEK_SET);
2040 if (res != (off_t)To)
2041 return filefd->FileFdError("Unable to seek to %llu", To);
2042 seekpos = To;
83e22e26 2043 buffer.reset();
fa89055f
DK
2044 return true;
2045 }
cabfb880 2046 virtual bool InternalSkip(unsigned long long Over) APT_OVERRIDE
fa89055f 2047 {
83e22e26 2048 if (Over >= buffer.size())
f63123c3 2049 {
83e22e26
JAK
2050 Over -= buffer.size();
2051 buffer.reset();
f63123c3
DK
2052 }
2053 else
2054 {
83e22e26 2055 buffer.bufferstart += Over;
f63123c3
DK
2056 return true;
2057 }
2058 if (Over == 0)
2059 return true;
fa89055f
DK
2060 off_t const res = lseek(filefd->iFd, Over, SEEK_CUR);
2061 if (res < 0)
2062 return filefd->FileFdError("Unable to seek ahead %llu",Over);
2063 seekpos = res;
2064 return true;
2065 }
cabfb880 2066 virtual bool InternalTruncate(unsigned long long const To) APT_OVERRIDE
fa89055f 2067 {
83e22e26 2068 if (buffer.size() != 0)
f63123c3
DK
2069 {
2070 unsigned long long const seekpos = lseek(filefd->iFd, 0, SEEK_CUR);
83e22e26
JAK
2071 if ((seekpos - buffer.size()) >= To)
2072 buffer.reset();
f63123c3 2073 else if (seekpos >= To)
83e22e26 2074 buffer.bufferend = (To - seekpos) + buffer.bufferstart;
f63123c3 2075 else
83e22e26 2076 buffer.reset();
f63123c3 2077 }
fa89055f
DK
2078 if (ftruncate(filefd->iFd, To) != 0)
2079 return filefd->FileFdError("Unable to truncate to %llu",To);
2080 return true;
2081 }
cabfb880 2082 virtual unsigned long long InternalTell() APT_OVERRIDE
fa89055f 2083 {
83e22e26 2084 return lseek(filefd->iFd,0,SEEK_CUR) - buffer.size();
fa89055f 2085 }
cabfb880 2086 virtual unsigned long long InternalSize() APT_OVERRIDE
fa89055f
DK
2087 {
2088 return filefd->FileSize();
2089 }
cabfb880
DK
2090 virtual bool InternalClose(std::string const &) APT_OVERRIDE { return true; }
2091 virtual bool InternalAlwaysAutoClose() const APT_OVERRIDE { return false; }
fa89055f 2092
11755147 2093 explicit DirectFileFdPrivate(FileFd * const filefd) : FileFdPrivate(filefd) {}
fa89055f 2094 virtual ~DirectFileFdPrivate() { InternalClose(""); }
4239dbca
DK
2095};
2096 /*}}}*/
6c55f07a
DK
2097// FileFd Constructors /*{{{*/
2098FileFd::FileFd(std::string FileName,unsigned int const Mode,unsigned long AccessMode) : iFd(-1), Flags(0), d(NULL)
2099{
2100 Open(FileName,Mode, None, AccessMode);
2101}
2102FileFd::FileFd(std::string FileName,unsigned int const Mode, CompressMode Compress, unsigned long AccessMode) : iFd(-1), Flags(0), d(NULL)
2103{
2104 Open(FileName,Mode, Compress, AccessMode);
2105}
2106FileFd::FileFd() : iFd(-1), Flags(AutoClose), d(NULL) {}
2107FileFd::FileFd(int const Fd, unsigned int const Mode, CompressMode Compress) : iFd(-1), Flags(0), d(NULL)
2108{
2109 OpenDescriptor(Fd, Mode, Compress);
2110}
2111FileFd::FileFd(int const Fd, bool const AutoClose) : iFd(-1), Flags(0), d(NULL)
2112{
2113 OpenDescriptor(Fd, ReadWrite, None, AutoClose);
2114}
2115 /*}}}*/
13d87e2e 2116// FileFd::Open - Open a file /*{{{*/
578bfd0a
AL
2117// ---------------------------------------------------------------------
2118/* The most commonly used open mode combinations are given with Mode */
e5f3f8c1 2119bool FileFd::Open(string FileName,unsigned int const Mode,CompressMode Compress, unsigned long const AccessMode)
578bfd0a 2120{
257e8d66 2121 if (Mode == ReadOnlyGzip)
e5f3f8c1 2122 return Open(FileName, ReadOnly, Gzip, AccessMode);
257e8d66 2123
468720c5 2124 if (Compress == Auto && (Mode & WriteOnly) == WriteOnly)
ae635e3c 2125 return FileFdError("Autodetection on %s only works in ReadOnly openmode!", FileName.c_str());
257e8d66 2126
468720c5
DK
2127 std::vector<APT::Configuration::Compressor> const compressors = APT::Configuration::getCompressors();
2128 std::vector<APT::Configuration::Compressor>::const_iterator compressor = compressors.begin();
2129 if (Compress == Auto)
2130 {
468720c5
DK
2131 for (; compressor != compressors.end(); ++compressor)
2132 {
e788a834 2133 std::string file = FileName + compressor->Extension;
468720c5
DK
2134 if (FileExists(file) == false)
2135 continue;
2136 FileName = file;
468720c5
DK
2137 break;
2138 }
2139 }
2140 else if (Compress == Extension)
2141 {
52b47296
DK
2142 std::string::size_type const found = FileName.find_last_of('.');
2143 std::string ext;
2144 if (found != std::string::npos)
2145 {
2146 ext = FileName.substr(found);
2147 if (ext == ".new" || ext == ".bak")
2148 {
2149 std::string::size_type const found2 = FileName.find_last_of('.', found - 1);
2150 if (found2 != std::string::npos)
2151 ext = FileName.substr(found2, found - found2);
2152 else
2153 ext.clear();
2154 }
2155 }
aee1aac6
DK
2156 for (; compressor != compressors.end(); ++compressor)
2157 if (ext == compressor->Extension)
2158 break;
2159 // no matching extension - assume uncompressed (imagine files like 'example.org_Packages')
2160 if (compressor == compressors.end())
2161 for (compressor = compressors.begin(); compressor != compressors.end(); ++compressor)
2162 if (compressor->Name == ".")
468720c5 2163 break;
468720c5 2164 }
aee1aac6 2165 else
468720c5
DK
2166 {
2167 std::string name;
2168 switch (Compress)
2169 {
aee1aac6 2170 case None: name = "."; break;
468720c5
DK
2171 case Gzip: name = "gzip"; break;
2172 case Bzip2: name = "bzip2"; break;
2173 case Lzma: name = "lzma"; break;
2174 case Xz: name = "xz"; break;
e3fbd54c 2175 case Lz4: name = "lz4"; break;
aee1aac6
DK
2176 case Auto:
2177 case Extension:
52b47296 2178 // Unreachable
ae635e3c 2179 return FileFdError("Opening File %s in None, Auto or Extension should be already handled?!?", FileName.c_str());
468720c5
DK
2180 }
2181 for (; compressor != compressors.end(); ++compressor)
2182 if (compressor->Name == name)
2183 break;
aee1aac6 2184 if (compressor == compressors.end())
ae635e3c 2185 return FileFdError("Can't find a configured compressor %s for file %s", name.c_str(), FileName.c_str());
468720c5
DK
2186 }
2187
aee1aac6 2188 if (compressor == compressors.end())
ae635e3c 2189 return FileFdError("Can't find a match for specified compressor mode for file %s", FileName.c_str());
e5f3f8c1 2190 return Open(FileName, Mode, *compressor, AccessMode);
aee1aac6 2191}
e5f3f8c1 2192bool FileFd::Open(string FileName,unsigned int const Mode,APT::Configuration::Compressor const &compressor, unsigned long const AccessMode)
aee1aac6
DK
2193{
2194 Close();
aee1aac6
DK
2195 Flags = AutoClose;
2196
2197 if ((Mode & WriteOnly) != WriteOnly && (Mode & (Atomic | Create | Empty | Exclusive)) != 0)
ae635e3c 2198 return FileFdError("ReadOnly mode for %s doesn't accept additional flags!", FileName.c_str());
aee1aac6 2199 if ((Mode & ReadWrite) == 0)
ae635e3c 2200 return FileFdError("No openmode provided in FileFd::Open for %s", FileName.c_str());
468720c5 2201
cd46d4eb
DK
2202 unsigned int OpenMode = Mode;
2203 if (FileName == "/dev/null")
2204 OpenMode = OpenMode & ~(Atomic | Exclusive | Create | Empty);
2205
2206 if ((OpenMode & Atomic) == Atomic)
257e8d66
DK
2207 {
2208 Flags |= Replace;
257e8d66 2209 }
cd46d4eb 2210 else if ((OpenMode & (Exclusive | Create)) == (Exclusive | Create))
257e8d66
DK
2211 {
2212 // for atomic, this will be done by rename in Close()
ce1f3a2c 2213 RemoveFile("FileFd::Open", FileName);
257e8d66 2214 }
cd46d4eb 2215 if ((OpenMode & Empty) == Empty)
578bfd0a 2216 {
257e8d66
DK
2217 struct stat Buf;
2218 if (lstat(FileName.c_str(),&Buf) == 0 && S_ISLNK(Buf.st_mode))
ce1f3a2c 2219 RemoveFile("FileFd::Open", FileName);
257e8d66 2220 }
c4fc2fd7 2221
561f860a 2222 int fileflags = 0;
cd46d4eb 2223 #define if_FLAGGED_SET(FLAG, MODE) if ((OpenMode & FLAG) == FLAG) fileflags |= MODE
561f860a
DK
2224 if_FLAGGED_SET(ReadWrite, O_RDWR);
2225 else if_FLAGGED_SET(ReadOnly, O_RDONLY);
2226 else if_FLAGGED_SET(WriteOnly, O_WRONLY);
4a9db827 2227
561f860a
DK
2228 if_FLAGGED_SET(Create, O_CREAT);
2229 if_FLAGGED_SET(Empty, O_TRUNC);
2230 if_FLAGGED_SET(Exclusive, O_EXCL);
561f860a 2231 #undef if_FLAGGED_SET
52b47296 2232
cd46d4eb 2233 if ((OpenMode & Atomic) == Atomic)
7335eebe
AGM
2234 {
2235 char *name = strdup((FileName + ".XXXXXX").c_str());
2236
dc545c0b 2237 if((iFd = mkstemp(name)) == -1)
7335eebe
AGM
2238 {
2239 free(name);
98b69f9d 2240 return FileFdErrno("mkstemp", "Could not create temporary file for %s", FileName.c_str());
7335eebe
AGM
2241 }
2242
2243 TemporaryFileName = string(name);
7335eebe 2244 free(name);
dc545c0b 2245
230e69d7
DK
2246 // umask() will always set the umask and return the previous value, so
2247 // we first set the umask and then reset it to the old value
2248 mode_t const CurrentUmask = umask(0);
2249 umask(CurrentUmask);
2250 // calculate the actual file permissions (just like open/creat)
2251 mode_t const FilePermissions = (AccessMode & ~CurrentUmask);
2252
2253 if(fchmod(iFd, FilePermissions) == -1)
dc545c0b 2254 return FileFdErrno("fchmod", "Could not change permissions for temporary file %s", TemporaryFileName.c_str());
7335eebe 2255 }
468720c5 2256 else
230e69d7 2257 iFd = open(FileName.c_str(), fileflags, AccessMode);
468720c5 2258
b711c01e 2259 this->FileName = FileName;
cd46d4eb 2260 if (iFd == -1 || OpenInternDescriptor(OpenMode, compressor) == false)
561f860a 2261 {
468720c5 2262 if (iFd != -1)
fc81e8f2 2263 {
561f860a
DK
2264 close (iFd);
2265 iFd = -1;
fc81e8f2 2266 }
ae635e3c 2267 return FileFdErrno("open",_("Could not open file %s"), FileName.c_str());
257e8d66 2268 }
578bfd0a 2269
13d87e2e
AL
2270 SetCloseExec(iFd,true);
2271 return true;
578bfd0a 2272}
257e8d66
DK
2273 /*}}}*/
2274// FileFd::OpenDescriptor - Open a filedescriptor /*{{{*/
52b47296 2275bool FileFd::OpenDescriptor(int Fd, unsigned int const Mode, CompressMode Compress, bool AutoClose)
aee1aac6
DK
2276{
2277 std::vector<APT::Configuration::Compressor> const compressors = APT::Configuration::getCompressors();
2278 std::vector<APT::Configuration::Compressor>::const_iterator compressor = compressors.begin();
2279 std::string name;
bce778a3
MV
2280
2281 // compat with the old API
2282 if (Mode == ReadOnlyGzip && Compress == None)
2283 Compress = Gzip;
2284
aee1aac6
DK
2285 switch (Compress)
2286 {
2287 case None: name = "."; break;
2288 case Gzip: name = "gzip"; break;
2289 case Bzip2: name = "bzip2"; break;
2290 case Lzma: name = "lzma"; break;
2291 case Xz: name = "xz"; break;
e3fbd54c 2292 case Lz4: name = "lz4"; break;
aee1aac6
DK
2293 case Auto:
2294 case Extension:
f97bb523
DK
2295 if (AutoClose == true && Fd != -1)
2296 close(Fd);
ae635e3c 2297 return FileFdError("Opening Fd %d in Auto or Extension compression mode is not supported", Fd);
aee1aac6
DK
2298 }
2299 for (; compressor != compressors.end(); ++compressor)
2300 if (compressor->Name == name)
2301 break;
2302 if (compressor == compressors.end())
f97bb523
DK
2303 {
2304 if (AutoClose == true && Fd != -1)
2305 close(Fd);
ae635e3c 2306 return FileFdError("Can't find a configured compressor %s for file %s", name.c_str(), FileName.c_str());
f97bb523 2307 }
aee1aac6
DK
2308 return OpenDescriptor(Fd, Mode, *compressor, AutoClose);
2309}
52b47296 2310bool FileFd::OpenDescriptor(int Fd, unsigned int const Mode, APT::Configuration::Compressor const &compressor, bool AutoClose)
144c0969
JAK
2311{
2312 Close();
2313 Flags = (AutoClose) ? FileFd::AutoClose : 0;
84baaae9 2314 iFd = Fd;
b711c01e 2315 this->FileName = "";
84baaae9 2316 if (OpenInternDescriptor(Mode, compressor) == false)
468720c5 2317 {
f97bb523 2318 if (iFd != -1 && (
84baaae9 2319 (Flags & Compressed) == Compressed ||
f97bb523
DK
2320 AutoClose == true))
2321 {
468720c5 2322 close (iFd);
f97bb523
DK
2323 iFd = -1;
2324 }
2325 return FileFdError(_("Could not open file descriptor %d"), Fd);
144c0969 2326 }
144c0969 2327 return true;
468720c5 2328}
52b47296 2329bool FileFd::OpenInternDescriptor(unsigned int const Mode, APT::Configuration::Compressor const &compressor)
468720c5 2330{
84baaae9
DK
2331 if (iFd == -1)
2332 return false;
ff477ee1 2333
fa89055f
DK
2334 if (d != nullptr)
2335 d->InternalClose(FileName);
2336
2337 if (d == nullptr)
2338 {
2339 if (false)
2340 /* dummy so that the rest can be 'else if's */;
2341#define APT_COMPRESS_INIT(NAME, CONSTRUCTOR) \
2342 else if (compressor.Name == NAME) \
2343 d = new CONSTRUCTOR(this)
69d6988a 2344#ifdef HAVE_ZLIB
fa89055f 2345 APT_COMPRESS_INIT("gzip", GzipFileFdPrivate);
69d6988a
DK
2346#endif
2347#ifdef HAVE_BZ2
fa89055f 2348 APT_COMPRESS_INIT("bzip2", Bz2FileFdPrivate);
69d6988a 2349#endif
7f350a37 2350#ifdef HAVE_LZMA
fa89055f
DK
2351 APT_COMPRESS_INIT("xz", LzmaFileFdPrivate);
2352 APT_COMPRESS_INIT("lzma", LzmaFileFdPrivate);
7f350a37 2353#endif
e3fbd54c
JAK
2354#ifdef HAVE_LZ4
2355 APT_COMPRESS_INIT("lz4", Lz4FileFdPrivate);
2356#endif
69d6988a 2357#undef APT_COMPRESS_INIT
fa89055f
DK
2358 else if (compressor.Name == "." || compressor.Binary.empty() == true)
2359 d = new DirectFileFdPrivate(this);
2360 else
2361 d = new PipedFileFdPrivate(this);
69d6988a 2362
88749b5d
JAK
2363 if (Mode & BufferedWrite)
2364 d = new BufferedWriteFileFdPrivate(d);
2365
1d68256d
JAK
2366 d->set_openmode(Mode);
2367 d->set_compressor(compressor);
fa89055f 2368 if ((Flags & AutoClose) != AutoClose && d->InternalAlwaysAutoClose())
84baaae9
DK
2369 {
2370 // Need to duplicate fd here or gz/bz2 close for cleanup will close the fd as well
2371 int const internFd = dup(iFd);
2372 if (internFd == -1)
2373 return FileFdErrno("OpenInternDescriptor", _("Could not open file descriptor %d"), iFd);
2374 iFd = internFd;
2375 }
561f860a 2376 }
fa89055f 2377 return d->InternalOpen(iFd, Mode);
144c0969 2378}
578bfd0a 2379 /*}}}*/
8e06abb2 2380// FileFd::~File - Closes the file /*{{{*/
578bfd0a
AL
2381// ---------------------------------------------------------------------
2382/* If the proper modes are selected then we close the Fd and possibly
2383 unlink the file on error. */
8e06abb2 2384FileFd::~FileFd()
578bfd0a
AL
2385{
2386 Close();
500400fe 2387 if (d != NULL)
fa89055f 2388 d->InternalClose(FileName);
96ab3c6f
MV
2389 delete d;
2390 d = NULL;
578bfd0a
AL
2391}
2392 /*}}}*/
8e06abb2 2393// FileFd::Read - Read a bit of the file /*{{{*/
578bfd0a 2394// ---------------------------------------------------------------------
1e3f4083 2395/* We are careful to handle interruption by a signal while reading
b0db36b1 2396 gracefully. */
650faab0 2397bool FileFd::Read(void *To,unsigned long long Size,unsigned long long *Actual)
578bfd0a 2398{
02c38073 2399 if (d == nullptr || Failed())
fa89055f 2400 return false;
39e77e45 2401 ssize_t Res = 1;
b0db36b1 2402 errno = 0;
f604cf55
AL
2403 if (Actual != 0)
2404 *Actual = 0;
699b209e 2405 *((char *)To) = '\0';
39e77e45 2406 while (Res > 0 && Size > 0)
578bfd0a 2407 {
fa89055f 2408 Res = d->InternalRead(To, Size);
b711c01e 2409
b0db36b1
AL
2410 if (Res < 0)
2411 {
b711c01e 2412 if (errno == EINTR)
c4b113e6
DK
2413 {
2414 // trick the while-loop into running again
2415 Res = 1;
2416 errno = 0;
b711c01e 2417 continue;
c4b113e6 2418 }
fa89055f 2419 return d->InternalReadError();
b0db36b1 2420 }
578bfd0a 2421
b0db36b1
AL
2422 To = (char *)To + Res;
2423 Size -= Res;
ff477ee1 2424 if (d != NULL)
1d68256d 2425 d->set_seekpos(d->get_seekpos() + Res);
f604cf55
AL
2426 if (Actual != 0)
2427 *Actual += Res;
b0db36b1 2428 }
b0db36b1
AL
2429
2430 if (Size == 0)
2431 return true;
2432
ddc1d8d0 2433 // Eof handling
f604cf55 2434 if (Actual != 0)
ddc1d8d0
AL
2435 {
2436 Flags |= HitEof;
2437 return true;
2438 }
ae635e3c
DK
2439
2440 return FileFdError(_("read, still have %llu to read but none left"), Size);
61db4824
DK
2441}
2442bool FileFd::Read(int const Fd, void *To, unsigned long long Size, unsigned long long * const Actual)
2443{
2444 ssize_t Res = 1;
2445 errno = 0;
2446 if (Actual != nullptr)
2447 *Actual = 0;
2448 *static_cast<char *>(To) = '\0';
2449 while (Res > 0 && Size > 0)
2450 {
2451 Res = read(Fd, To, Size);
2452 if (Res < 0)
2453 {
2454 if (errno == EINTR)
2455 {
2456 Res = 1;
2457 errno = 0;
2458 continue;
2459 }
2460 return _error->Errno("read", _("Read error"));
2461 }
2462 To = static_cast<char *>(To) + Res;
2463 Size -= Res;
2464 if (Actual != 0)
2465 *Actual += Res;
2466 }
2467 if (Size == 0)
2468 return true;
2469 if (Actual != nullptr)
2470 return true;
2471 return _error->Error(_("read, still have %llu to read but none left"), Size);
578bfd0a
AL
2472}
2473 /*}}}*/
032bd56f
DK
2474// FileFd::ReadLine - Read a complete line from the file /*{{{*/
2475// ---------------------------------------------------------------------
fa89055f 2476/* Beware: This method can be quite slow for big buffers on UNcompressed
032bd56f
DK
2477 files because of the naive implementation! */
2478char* FileFd::ReadLine(char *To, unsigned long long const Size)
2479{
699b209e 2480 *To = '\0';
02c38073 2481 if (d == nullptr || Failed())
fa89055f
DK
2482 return nullptr;
2483 return d->InternalReadLine(To, Size);
032bd56f
DK
2484}
2485 /*}}}*/
766761fd
JAK
2486// FileFd::Flush - Flush the file /*{{{*/
2487bool FileFd::Flush()
2488{
02c38073
DK
2489 if (Failed())
2490 return false;
766761fd
JAK
2491 if (d == nullptr)
2492 return true;
2493
2494 return d->InternalFlush();
2495}
2496 /*}}}*/
8e06abb2 2497// FileFd::Write - Write to the file /*{{{*/
650faab0 2498bool FileFd::Write(const void *From,unsigned long long Size)
578bfd0a 2499{
02c38073 2500 if (d == nullptr || Failed())
fa89055f 2501 return false;
5df91bc7 2502 ssize_t Res = 1;
b0db36b1 2503 errno = 0;
5df91bc7 2504 while (Res > 0 && Size > 0)
578bfd0a 2505 {
fa89055f 2506 Res = d->InternalWrite(From, Size);
9a63c3f4 2507
b0db36b1 2508 if (Res < 0)
9a63c3f4
CW
2509 {
2510 if (errno == EINTR)
2511 {
2512 // trick the while-loop into running again
2513 Res = 1;
2514 errno = 0;
2515 continue;
2516 }
fa89055f 2517 return d->InternalWriteError();
9a63c3f4 2518 }
fa89055f 2519
cf4ff3b7 2520 From = (char const *)From + Res;
b0db36b1 2521 Size -= Res;
ff477ee1 2522 if (d != NULL)
1d68256d 2523 d->set_seekpos(d->get_seekpos() + Res);
578bfd0a 2524 }
fa89055f 2525
b0db36b1
AL
2526 if (Size == 0)
2527 return true;
ae635e3c
DK
2528
2529 return FileFdError(_("write, still have %llu to write but couldn't"), Size);
d68d65ad
DK
2530}
2531bool FileFd::Write(int Fd, const void *From, unsigned long long Size)
2532{
5df91bc7 2533 ssize_t Res = 1;
d68d65ad 2534 errno = 0;
5df91bc7 2535 while (Res > 0 && Size > 0)
d68d65ad
DK
2536 {
2537 Res = write(Fd,From,Size);
2538 if (Res < 0 && errno == EINTR)
2539 continue;
2540 if (Res < 0)
2541 return _error->Errno("write",_("Write error"));
2542
cf4ff3b7 2543 From = (char const *)From + Res;
d68d65ad
DK
2544 Size -= Res;
2545 }
d68d65ad
DK
2546
2547 if (Size == 0)
2548 return true;
2549
2550 return _error->Error(_("write, still have %llu to write but couldn't"), Size);
578bfd0a
AL
2551}
2552 /*}}}*/
8e06abb2 2553// FileFd::Seek - Seek in the file /*{{{*/
650faab0 2554bool FileFd::Seek(unsigned long long To)
578bfd0a 2555{
02c38073 2556 if (d == nullptr || Failed())
fa89055f 2557 return false;
bb93178b 2558 Flags &= ~HitEof;
fa89055f 2559 return d->InternalSeek(To);
727f18af
AL
2560}
2561 /*}}}*/
fa89055f 2562// FileFd::Skip - Skip over data in the file /*{{{*/
650faab0 2563bool FileFd::Skip(unsigned long long Over)
727f18af 2564{
02c38073 2565 if (d == nullptr || Failed())
fa89055f
DK
2566 return false;
2567 return d->InternalSkip(Over);
6d5dd02a
AL
2568}
2569 /*}}}*/
fa89055f 2570// FileFd::Truncate - Truncate the file /*{{{*/
650faab0 2571bool FileFd::Truncate(unsigned long long To)
6d5dd02a 2572{
02c38073 2573 if (d == nullptr || Failed())
fa89055f 2574 return false;
ad5051ef
DK
2575 // truncating /dev/null is always successful - as we get an error otherwise
2576 if (To == 0 && FileName == "/dev/null")
2577 return true;
fa89055f 2578 return d->InternalTruncate(To);
578bfd0a
AL
2579}
2580 /*}}}*/
7f25bdff
AL
2581// FileFd::Tell - Current seek position /*{{{*/
2582// ---------------------------------------------------------------------
2583/* */
650faab0 2584unsigned long long FileFd::Tell()
7f25bdff 2585{
02c38073 2586 if (d == nullptr || Failed())
fa89055f
DK
2587 return false;
2588 off_t const Res = d->InternalTell();
7f25bdff 2589 if (Res == (off_t)-1)
ae635e3c 2590 FileFdErrno("lseek","Failed to determine the current file position");
1d68256d 2591 d->set_seekpos(Res);
7f25bdff
AL
2592 return Res;
2593}
2594 /*}}}*/
8190b07a 2595static bool StatFileFd(char const * const msg, int const iFd, std::string const &FileName, struct stat &Buf, FileFdPrivate * const d) /*{{{*/
578bfd0a 2596{
1d68256d 2597 bool ispipe = (d != NULL && d->get_is_pipe() == true);
6008b79a
DK
2598 if (ispipe == false)
2599 {
2600 if (fstat(iFd,&Buf) != 0)
8190b07a
DK
2601 // higher-level code will generate more meaningful messages,
2602 // even translated this would be meaningless for users
2603 return _error->Errno("fstat", "Unable to determine %s for fd %i", msg, iFd);
003c40d3
DK
2604 if (FileName.empty() == false)
2605 ispipe = S_ISFIFO(Buf.st_mode);
6008b79a 2606 }
699b209e
DK
2607
2608 // for compressor pipes st_size is undefined and at 'best' zero
6008b79a 2609 if (ispipe == true)
699b209e
DK
2610 {
2611 // we set it here, too, as we get the info here for free
2612 // in theory the Open-methods should take care of it already
ff477ee1 2613 if (d != NULL)
1d68256d 2614 d->set_is_pipe(true);
699b209e 2615 if (stat(FileName.c_str(), &Buf) != 0)
8190b07a
DK
2616 return _error->Errno("fstat", "Unable to determine %s for file %s", msg, FileName.c_str());
2617 }
2618 return true;
2619}
2620 /*}}}*/
2621// FileFd::FileSize - Return the size of the file /*{{{*/
2622unsigned long long FileFd::FileSize()
2623{
2624 struct stat Buf;
2625 if (StatFileFd("file size", iFd, FileName, Buf, d) == false)
2626 {
2627 Flags |= Fail;
2628 return 0;
699b209e 2629 }
4260fd39
DK
2630 return Buf.st_size;
2631}
2632 /*}}}*/
8190b07a
DK
2633// FileFd::ModificationTime - Return the time of last touch /*{{{*/
2634time_t FileFd::ModificationTime()
2635{
2636 struct stat Buf;
2637 if (StatFileFd("modification time", iFd, FileName, Buf, d) == false)
2638 {
2639 Flags |= Fail;
2640 return 0;
2641 }
2642 return Buf.st_mtime;
2643}
2644 /*}}}*/
4260fd39 2645// FileFd::Size - Return the size of the content in the file /*{{{*/
650faab0 2646unsigned long long FileFd::Size()
4260fd39 2647{
fa89055f 2648 if (d == nullptr)
02c38073 2649 return 0;
fa89055f 2650 return d->InternalSize();
578bfd0a
AL
2651}
2652 /*}}}*/
8e06abb2 2653// FileFd::Close - Close the file if the close flag is set /*{{{*/
578bfd0a
AL
2654// ---------------------------------------------------------------------
2655/* */
8e06abb2 2656bool FileFd::Close()
578bfd0a 2657{
f43dd439 2658 if (Failed() == false && Flush() == false)
766761fd 2659 return false;
032bd56f
DK
2660 if (iFd == -1)
2661 return true;
2662
578bfd0a
AL
2663 bool Res = true;
2664 if ((Flags & AutoClose) == AutoClose)
d13c2d3f 2665 {
500400fe
DK
2666 if ((Flags & Compressed) != Compressed && iFd > 0 && close(iFd) != 0)
2667 Res &= _error->Errno("close",_("Problem closing the file %s"), FileName.c_str());
2da8aae5
JAK
2668 }
2669
2670 if (d != NULL)
2671 {
fa89055f 2672 Res &= d->InternalClose(FileName);
2da8aae5
JAK
2673 delete d;
2674 d = NULL;
d13c2d3f 2675 }
3010fb0e 2676
d3aac32e 2677 if ((Flags & Replace) == Replace) {
fc5db01b 2678 if (Failed() == false && rename(TemporaryFileName.c_str(), FileName.c_str()) != 0)
62d073d9
DK
2679 Res &= _error->Errno("rename",_("Problem renaming the file %s to %s"), TemporaryFileName.c_str(), FileName.c_str());
2680
fd3b761e 2681 FileName = TemporaryFileName; // for the unlink() below.
257e8d66 2682 TemporaryFileName.clear();
3010fb0e 2683 }
62d073d9
DK
2684
2685 iFd = -1;
2686
578bfd0a
AL
2687 if ((Flags & Fail) == Fail && (Flags & DelOnFail) == DelOnFail &&
2688 FileName.empty() == false)
ce1f3a2c 2689 Res &= RemoveFile("FileFd::Close", FileName);
3010fb0e 2690
fbb89d94
DK
2691 if (Res == false)
2692 Flags |= Fail;
578bfd0a
AL
2693 return Res;
2694}
2695 /*}}}*/
b2e465d6
AL
2696// FileFd::Sync - Sync the file /*{{{*/
2697// ---------------------------------------------------------------------
2698/* */
2699bool FileFd::Sync()
2700{
b2e465d6 2701 if (fsync(iFd) != 0)
ae635e3c
DK
2702 return FileFdErrno("sync",_("Problem syncing the file"));
2703 return true;
2704}
2705 /*}}}*/
2706// FileFd::FileFdErrno - set Fail and call _error->Errno *{{{*/
2707bool FileFd::FileFdErrno(const char *Function, const char *Description,...)
2708{
2709 Flags |= Fail;
2710 va_list args;
2711 size_t msgSize = 400;
2712 int const errsv = errno;
196d590a
DK
2713 bool retry;
2714 do {
ae635e3c 2715 va_start(args,Description);
196d590a 2716 retry = _error->InsertErrno(GlobalError::ERROR, Function, Description, args, errsv, msgSize);
ae635e3c 2717 va_end(args);
196d590a 2718 } while (retry);
ae635e3c
DK
2719 return false;
2720}
2721 /*}}}*/
2722// FileFd::FileFdError - set Fail and call _error->Error *{{{*/
2723bool FileFd::FileFdError(const char *Description,...) {
2724 Flags |= Fail;
2725 va_list args;
2726 size_t msgSize = 400;
196d590a
DK
2727 bool retry;
2728 do {
ae635e3c 2729 va_start(args,Description);
196d590a 2730 retry = _error->Insert(GlobalError::ERROR, Description, args, msgSize);
ae635e3c 2731 va_end(args);
196d590a 2732 } while (retry);
ae635e3c 2733 return false;
b2e465d6
AL
2734}
2735 /*}}}*/
fa89055f 2736gzFile FileFd::gzFd() { /*{{{*/
7f350a37 2737#ifdef HAVE_ZLIB
fa89055f
DK
2738 GzipFileFdPrivate * const gzipd = dynamic_cast<GzipFileFdPrivate*>(d);
2739 if (gzipd == nullptr)
2740 return nullptr;
2741 else
2742 return gzipd->gz;
7f350a37 2743#else
fa89055f 2744 return nullptr;
7f350a37
DK
2745#endif
2746}
fa89055f 2747 /*}}}*/
8d01b9d6 2748
f8aba23f 2749// Glob - wrapper around "glob()" /*{{{*/
8d01b9d6
MV
2750std::vector<std::string> Glob(std::string const &pattern, int flags)
2751{
2752 std::vector<std::string> result;
2753 glob_t globbuf;
ec4835a1
ÁGM
2754 int glob_res;
2755 unsigned int i;
8d01b9d6
MV
2756
2757 glob_res = glob(pattern.c_str(), flags, NULL, &globbuf);
2758
2759 if (glob_res != 0)
2760 {
2761 if(glob_res != GLOB_NOMATCH) {
2762 _error->Errno("glob", "Problem with glob");
2763 return result;
2764 }
2765 }
2766
2767 // append results
2768 for(i=0;i<globbuf.gl_pathc;i++)
2769 result.push_back(string(globbuf.gl_pathv[i]));
2770
2771 globfree(&globbuf);
2772 return result;
2773}
2774 /*}}}*/
e2c8c825 2775static std::string APT_NONNULL(1) GetTempDirEnv(char const * const env) /*{{{*/
68e01721 2776{
e2c8c825 2777 const char *tmpdir = getenv(env);
68e01721
MV
2778
2779#ifdef P_tmpdir
2780 if (!tmpdir)
2781 tmpdir = P_tmpdir;
2782#endif
2783
68e01721 2784 struct stat st;
0d303f17 2785 if (!tmpdir || strlen(tmpdir) == 0 || // tmpdir is set
dd6da7d2
DK
2786 stat(tmpdir, &st) != 0 || (st.st_mode & S_IFDIR) == 0) // exists and is directory
2787 tmpdir = "/tmp";
2788 else if (geteuid() != 0 && // root can do everything anyway
2789 faccessat(-1, tmpdir, R_OK | W_OK | X_OK, AT_EACCESS | AT_SYMLINK_NOFOLLOW) != 0) // current user has rwx access to directory
68e01721
MV
2790 tmpdir = "/tmp";
2791
2792 return string(tmpdir);
e2c8c825
DK
2793}
2794 /*}}}*/
2795std::string GetTempDir() /*{{{*/
2796{
2797 return GetTempDirEnv("TMPDIR");
dd6da7d2
DK
2798}
2799std::string GetTempDir(std::string const &User)
2800{
2801 // no need/possibility to drop privs
2802 if(getuid() != 0 || User.empty() || User == "root")
2803 return GetTempDir();
2804
2805 struct passwd const * const pw = getpwnam(User.c_str());
2806 if (pw == NULL)
2807 return GetTempDir();
2808
226c0f64
DK
2809 gid_t const old_euid = geteuid();
2810 gid_t const old_egid = getegid();
dd6da7d2
DK
2811 if (setegid(pw->pw_gid) != 0)
2812 _error->Errno("setegid", "setegid %u failed", pw->pw_gid);
2813 if (seteuid(pw->pw_uid) != 0)
2814 _error->Errno("seteuid", "seteuid %u failed", pw->pw_uid);
2815
2816 std::string const tmp = GetTempDir();
2817
226c0f64
DK
2818 if (seteuid(old_euid) != 0)
2819 _error->Errno("seteuid", "seteuid %u failed", old_euid);
2820 if (setegid(old_egid) != 0)
2821 _error->Errno("setegid", "setegid %u failed", old_egid);
dd6da7d2
DK
2822
2823 return tmp;
68e01721 2824}
f8aba23f 2825 /*}}}*/
c9443c01 2826FileFd* GetTempFile(std::string const &Prefix, bool ImmediateUnlink, FileFd * const TmpFd) /*{{{*/
0d29b9d4
MV
2827{
2828 char fn[512];
c9443c01 2829 FileFd * const Fd = TmpFd == NULL ? new FileFd() : TmpFd;
0d29b9d4 2830
c9443c01
DK
2831 std::string const tempdir = GetTempDir();
2832 snprintf(fn, sizeof(fn), "%s/%s.XXXXXX",
0d29b9d4 2833 tempdir.c_str(), Prefix.c_str());
c9443c01 2834 int const fd = mkstemp(fn);
0d29b9d4
MV
2835 if(ImmediateUnlink)
2836 unlink(fn);
c9443c01 2837 if (fd < 0)
0d29b9d4
MV
2838 {
2839 _error->Errno("GetTempFile",_("Unable to mkstemp %s"), fn);
2840 return NULL;
2841 }
c9443c01 2842 if (!Fd->OpenDescriptor(fd, FileFd::ReadWrite, FileFd::None, true))
0d29b9d4
MV
2843 {
2844 _error->Errno("GetTempFile",_("Unable to write to %s"),fn);
2845 return NULL;
2846 }
0d29b9d4
MV
2847 return Fd;
2848}
f8aba23f
DK
2849 /*}}}*/
2850bool Rename(std::string From, std::string To) /*{{{*/
c1409d1b
MV
2851{
2852 if (rename(From.c_str(),To.c_str()) != 0)
2853 {
2854 _error->Error(_("rename failed, %s (%s -> %s)."),strerror(errno),
2855 From.c_str(),To.c_str());
2856 return false;
f8aba23f 2857 }
c1409d1b
MV
2858 return true;
2859}
f8aba23f
DK
2860 /*}}}*/
2861bool Popen(const char* Args[], FileFd &Fd, pid_t &Child, FileFd::OpenMode Mode)/*{{{*/
0ecceb5b
JAK
2862{
2863 return Popen(Args, Fd, Child, Mode, true);
2864}
2865 /*}}}*/
2866bool Popen(const char* Args[], FileFd &Fd, pid_t &Child, FileFd::OpenMode Mode, bool CaptureStderr)/*{{{*/
7ad2a347
MV
2867{
2868 int fd;
2869 if (Mode != FileFd::ReadOnly && Mode != FileFd::WriteOnly)
2870 return _error->Error("Popen supports ReadOnly (x)or WriteOnly mode only");
2871
2872 int Pipe[2] = {-1, -1};
2873 if(pipe(Pipe) != 0)
7ad2a347 2874 return _error->Errno("pipe", _("Failed to create subprocess IPC"));
5e49cbb7 2875
7ad2a347
MV
2876 std::set<int> keep_fds;
2877 keep_fds.insert(Pipe[0]);
2878 keep_fds.insert(Pipe[1]);
2879 Child = ExecFork(keep_fds);
2880 if(Child < 0)
2881 return _error->Errno("fork", "Failed to fork");
2882 if(Child == 0)
2883 {
2884 if(Mode == FileFd::ReadOnly)
2885 {
2886 close(Pipe[0]);
2887 fd = Pipe[1];
2888 }
2889 else if(Mode == FileFd::WriteOnly)
2890 {
2891 close(Pipe[1]);
2892 fd = Pipe[0];
2893 }
2894
2895 if(Mode == FileFd::ReadOnly)
2896 {
2897 dup2(fd, 1);
0ecceb5b
JAK
2898 if (CaptureStderr == true)
2899 dup2(fd, 2);
7ad2a347
MV
2900 } else if(Mode == FileFd::WriteOnly)
2901 dup2(fd, 0);
2902
2903 execv(Args[0], (char**)Args);
2904 _exit(100);
2905 }
2906 if(Mode == FileFd::ReadOnly)
2907 {
2908 close(Pipe[1]);
2909 fd = Pipe[0];
8f5b67ae
DK
2910 }
2911 else if(Mode == FileFd::WriteOnly)
7ad2a347
MV
2912 {
2913 close(Pipe[0]);
2914 fd = Pipe[1];
2915 }
8f5b67ae
DK
2916 else
2917 return _error->Error("Popen supports ReadOnly (x)or WriteOnly mode only");
7ad2a347
MV
2918 Fd.OpenDescriptor(fd, Mode, FileFd::None, true);
2919
2920 return true;
2921}
f8aba23f
DK
2922 /*}}}*/
2923bool DropPrivileges() /*{{{*/
fc1a78d8 2924{
8f45798d
DK
2925 if(_config->FindB("Debug::NoDropPrivs", false) == true)
2926 return true;
2927
2928#if __gnu_linux__
2929#if defined(PR_SET_NO_NEW_PRIVS) && ( PR_SET_NO_NEW_PRIVS != 38 )
2930#error "PR_SET_NO_NEW_PRIVS is defined, but with a different value than expected!"
2931#endif
2932 // see prctl(2), needs linux3.5 at runtime - magic constant to avoid it at buildtime
2933 int ret = prctl(38, 1, 0, 0, 0);
2934 // ignore EINVAL - kernel is too old to understand the option
2935 if(ret < 0 && errno != EINVAL)
2936 _error->Warning("PR_SET_NO_NEW_PRIVS failed with %i", ret);
2937#endif
2938
990dd78a
DK
2939 // empty setting disables privilege dropping - this also ensures
2940 // backward compatibility, see bug #764506
2941 const std::string toUser = _config->Find("APT::Sandbox::User");
514a25cb 2942 if (toUser.empty() || toUser == "root")
990dd78a
DK
2943 return true;
2944
ebca2f25
DK
2945 // a lot can go wrong trying to drop privileges completely,
2946 // so ideally we would like to verify that we have done it –
2947 // but the verify asks for too much in case of fakeroot (and alike)
2948 // [Specific checks can be overridden with dedicated options]
2949 bool const VerifySandboxing = _config->FindB("APT::Sandbox::Verify", false);
2950
f1e3c8f0 2951 // uid will be 0 in the end, but gid might be different anyway
8f45798d
DK
2952 uid_t const old_uid = getuid();
2953 gid_t const old_gid = getgid();
fc1a78d8 2954
5f2047ec
JAK
2955 if (old_uid != 0)
2956 return true;
3927c6da 2957
b8dae9a1 2958 struct passwd *pw = getpwnam(toUser.c_str());
fc1a78d8 2959 if (pw == NULL)
b8dae9a1 2960 return _error->Error("No user %s, can not drop rights", toUser.c_str());
3927c6da 2961
f1e3c8f0 2962 // Do not change the order here, it might break things
5a326439 2963 // Get rid of all our supplementary groups first
3b084f06 2964 if (setgroups(1, &pw->pw_gid))
3927c6da
MV
2965 return _error->Errno("setgroups", "Failed to setgroups");
2966
5a326439
JAK
2967 // Now change the group ids to the new user
2968#ifdef HAVE_SETRESGID
2969 if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) != 0)
2970 return _error->Errno("setresgid", "Failed to set new group ids");
2971#else
3927c6da 2972 if (setegid(pw->pw_gid) != 0)
5f2047ec
JAK
2973 return _error->Errno("setegid", "Failed to setegid");
2974
fc1a78d8
MV
2975 if (setgid(pw->pw_gid) != 0)
2976 return _error->Errno("setgid", "Failed to setgid");
5a326439 2977#endif
5f2047ec 2978
5a326439
JAK
2979 // Change the user ids to the new user
2980#ifdef HAVE_SETRESUID
2981 if (setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) != 0)
2982 return _error->Errno("setresuid", "Failed to set new user ids");
2983#else
fc1a78d8
MV
2984 if (setuid(pw->pw_uid) != 0)
2985 return _error->Errno("setuid", "Failed to setuid");
5f2047ec
JAK
2986 if (seteuid(pw->pw_uid) != 0)
2987 return _error->Errno("seteuid", "Failed to seteuid");
5a326439 2988#endif
5f2047ec 2989
ebca2f25
DK
2990 // disabled by default as fakeroot doesn't implement getgroups currently (#806521)
2991 if (VerifySandboxing == true || _config->FindB("APT::Sandbox::Verify::Groups", false) == true)
2992 {
2993 // Verify that the user isn't still in any supplementary groups
2994 long const ngroups_max = sysconf(_SC_NGROUPS_MAX);
2995 std::unique_ptr<gid_t[]> gidlist(new gid_t[ngroups_max]);
2996 if (unlikely(gidlist == NULL))
2997 return _error->Error("Allocation of a list of size %lu for getgroups failed", ngroups_max);
2998 ssize_t gidlist_nr;
2999 if ((gidlist_nr = getgroups(ngroups_max, gidlist.get())) < 0)
3000 return _error->Errno("getgroups", "Could not get new groups (%lu)", ngroups_max);
3001 for (ssize_t i = 0; i < gidlist_nr; ++i)
3002 if (gidlist[i] != pw->pw_gid)
3003 return _error->Error("Could not switch group, user %s is still in group %d", toUser.c_str(), gidlist[i]);
3004 }
3005
3006 // enabled by default as all fakeroot-lookalikes should fake that accordingly
3007 if (VerifySandboxing == true || _config->FindB("APT::Sandbox::Verify::IDs", true) == true)
3008 {
3009 // Verify that gid, egid, uid, and euid changed
3010 if (getgid() != pw->pw_gid)
3011 return _error->Error("Could not switch group");
3012 if (getegid() != pw->pw_gid)
3013 return _error->Error("Could not switch effective group");
3014 if (getuid() != pw->pw_uid)
3015 return _error->Error("Could not switch user");
3016 if (geteuid() != pw->pw_uid)
3017 return _error->Error("Could not switch effective user");
5f2047ec 3018
550ab420 3019#ifdef HAVE_GETRESUID
ebca2f25
DK
3020 // verify that the saved set-user-id was changed as well
3021 uid_t ruid = 0;
3022 uid_t euid = 0;
3023 uid_t suid = 0;
3024 if (getresuid(&ruid, &euid, &suid))
3025 return _error->Errno("getresuid", "Could not get saved set-user-ID");
3026 if (suid != pw->pw_uid)
3027 return _error->Error("Could not switch saved set-user-ID");
550ab420
JAK
3028#endif
3029
3030#ifdef HAVE_GETRESGID
ebca2f25
DK
3031 // verify that the saved set-group-id was changed as well
3032 gid_t rgid = 0;
3033 gid_t egid = 0;
3034 gid_t sgid = 0;
3035 if (getresgid(&rgid, &egid, &sgid))
3036 return _error->Errno("getresuid", "Could not get saved set-group-ID");
3037 if (sgid != pw->pw_gid)
3038 return _error->Error("Could not switch saved set-group-ID");
550ab420 3039#endif
ebca2f25 3040 }
550ab420 3041
ebca2f25
DK
3042 // disabled as fakeroot doesn't forbid (by design) (re)gaining root from unprivileged
3043 if (VerifySandboxing == true || _config->FindB("APT::Sandbox::Verify::Regain", false) == true)
3044 {
3045 // Check that uid and gid changes do not work anymore
3046 if (pw->pw_gid != old_gid && (setgid(old_gid) != -1 || setegid(old_gid) != -1))
3047 return _error->Error("Could restore a gid to root, privilege dropping did not work");
bdc00df5 3048
ebca2f25
DK
3049 if (pw->pw_uid != old_uid && (setuid(old_uid) != -1 || seteuid(old_uid) != -1))
3050 return _error->Error("Could restore a uid to root, privilege dropping did not work");
3051 }
bdc00df5 3052
34b491e7
DK
3053 if (_config->FindB("APT::Sandbox::ResetEnvironment", true))
3054 {
3055 setenv("HOME", pw->pw_dir, 1);
3056 setenv("USER", pw->pw_name, 1);
3057 setenv("USERNAME", pw->pw_name, 1);
3058 setenv("LOGNAME", pw->pw_name, 1);
3059 auto const shell = flNotDir(pw->pw_shell);
3060 if (shell == "false" || shell == "nologin")
3061 setenv("SHELL", "/bin/sh", 1);
3062 else
3063 setenv("SHELL", pw->pw_shell, 1);
e2c8c825
DK
3064 auto const apt_setenv_tmp = [](char const * const env) {
3065 auto const tmpdir = getenv(env);
3066 if (tmpdir != nullptr)
3067 {
3068 auto const ourtmpdir = GetTempDirEnv(env);
3069 if (ourtmpdir != tmpdir)
3070 setenv(env, ourtmpdir.c_str(), 1);
3071 }
3072 };
3073 apt_setenv_tmp("TMPDIR");
3074 apt_setenv_tmp("TEMPDIR");
3075 apt_setenv_tmp("TMP");
3076 apt_setenv_tmp("TEMP");
34b491e7
DK
3077 }
3078
fc1a78d8
MV
3079 return true;
3080}
f8aba23f 3081 /*}}}*/