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