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