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