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