]> git.saurik.com Git - apt.git/blame - apt-pkg/contrib/fileutl.cc
Merge branch 'debian/sid' into debian/experimental
[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
54676e1a 803 // Close all of our FDs - just in case
61f954bf 804 for (int K = 3; K != sysconf(_SC_OPEN_MAX); K++)
75ef8f14
MV
805 {
806 if(KeepFDs.find(K) == KeepFDs.end())
007dc9e0 807 fcntl(K,F_SETFD,FD_CLOEXEC);
75ef8f14 808 }
54676e1a
AL
809 }
810
811 return Process;
812}
813 /*}}}*/
ddc1d8d0
AL
814// ExecWait - Fancy waitpid /*{{{*/
815// ---------------------------------------------------------------------
2c9a72d1 816/* Waits for the given sub process. If Reap is set then no errors are
ddc1d8d0
AL
817 generated. Otherwise a failed subprocess will generate a proper descriptive
818 message */
3826564e 819bool ExecWait(pid_t Pid,const char *Name,bool Reap)
ddc1d8d0
AL
820{
821 if (Pid <= 1)
822 return true;
823
824 // Wait and collect the error code
825 int Status;
826 while (waitpid(Pid,&Status,0) != Pid)
827 {
828 if (errno == EINTR)
829 continue;
830
831 if (Reap == true)
832 return false;
833
db0db9fe 834 return _error->Error(_("Waited for %s but it wasn't there"),Name);
ddc1d8d0
AL
835 }
836
837
838 // Check for an error code.
839 if (WIFEXITED(Status) == 0 || WEXITSTATUS(Status) != 0)
840 {
841 if (Reap == true)
842 return false;
ab7f4d7c 843 if (WIFSIGNALED(Status) != 0)
40e7fe0e 844 {
ab7f4d7c
MV
845 if( WTERMSIG(Status) == SIGSEGV)
846 return _error->Error(_("Sub-process %s received a segmentation fault."),Name);
847 else
848 return _error->Error(_("Sub-process %s received signal %u."),Name, WTERMSIG(Status));
40e7fe0e 849 }
ddc1d8d0
AL
850
851 if (WIFEXITED(Status) != 0)
b2e465d6 852 return _error->Error(_("Sub-process %s returned an error code (%u)"),Name,WEXITSTATUS(Status));
ddc1d8d0 853
b2e465d6 854 return _error->Error(_("Sub-process %s exited unexpectedly"),Name);
ddc1d8d0
AL
855 }
856
857 return true;
858}
859 /*}}}*/
f8aba23f 860// StartsWithGPGClearTextSignature - Check if a file is Pgp/GPG clearsigned /*{{{*/
fe5804fc 861bool StartsWithGPGClearTextSignature(string const &FileName)
0854ad8b
MV
862{
863 static const char* SIGMSG = "-----BEGIN PGP SIGNED MESSAGE-----\n";
1c89c98a 864 char buffer[strlen(SIGMSG)+1];
0854ad8b
MV
865 FILE* gpg = fopen(FileName.c_str(), "r");
866 if (gpg == NULL)
867 return false;
868
869 char const * const test = fgets(buffer, sizeof(buffer), gpg);
870 fclose(gpg);
871 if (test == NULL || strcmp(buffer, SIGMSG) != 0)
872 return false;
873
874 return true;
875}
f8aba23f 876 /*}}}*/
0854ad8b 877
4239dbca
DK
878class FileFdPrivate { /*{{{*/
879 public:
880#ifdef HAVE_ZLIB
881 gzFile gz;
882#endif
883#ifdef HAVE_BZ2
884 BZFILE* bz2;
885#endif
886#ifdef HAVE_LZMA
887 struct LZMAFILE {
888 FILE* file;
889 uint8_t buffer[4096];
890 lzma_stream stream;
891 lzma_ret err;
892 bool eof;
893 bool compressing;
894
25613a61 895 LZMAFILE() : file(NULL), eof(false), compressing(false) { buffer[0] = '\0'; }
4239dbca
DK
896 ~LZMAFILE() {
897 if (compressing == true)
898 {
899 for (;;) {
900 stream.avail_out = sizeof(buffer)/sizeof(buffer[0]);
901 stream.next_out = buffer;
902 err = lzma_code(&stream, LZMA_FINISH);
903 if (err != LZMA_OK && err != LZMA_STREAM_END)
904 {
905 _error->Error("~LZMAFILE: Compress finalisation failed");
906 break;
907 }
908 size_t const n = sizeof(buffer)/sizeof(buffer[0]) - stream.avail_out;
909 if (n && fwrite(buffer, 1, n, file) != n)
910 {
911 _error->Errno("~LZMAFILE",_("Write error"));
912 break;
913 }
914 if (err == LZMA_STREAM_END)
915 break;
916 }
917 }
918 lzma_end(&stream);
919 fclose(file);
920 }
921 };
922 LZMAFILE* lzma;
923#endif
924 int compressed_fd;
925 pid_t compressor_pid;
926 bool pipe;
927 APT::Configuration::Compressor compressor;
928 unsigned int openmode;
929 unsigned long long seekpos;
930 FileFdPrivate() :
931#ifdef HAVE_ZLIB
932 gz(NULL),
933#endif
934#ifdef HAVE_BZ2
935 bz2(NULL),
936#endif
937#ifdef HAVE_LZMA
938 lzma(NULL),
939#endif
940 compressed_fd(-1), compressor_pid(-1), pipe(false),
941 openmode(0), seekpos(0) {};
942 bool InternalClose(std::string const &FileName)
943 {
944 if (false)
945 /* dummy so that the rest can be 'else if's */;
946#ifdef HAVE_ZLIB
947 else if (gz != NULL) {
948 int const e = gzclose(gz);
949 gz = NULL;
950 // gzdclose() on empty files always fails with "buffer error" here, ignore that
951 if (e != 0 && e != Z_BUF_ERROR)
952 return _error->Errno("close",_("Problem closing the gzip file %s"), FileName.c_str());
953 }
954#endif
955#ifdef HAVE_BZ2
956 else if (bz2 != NULL) {
957 BZ2_bzclose(bz2);
958 bz2 = NULL;
959 }
960#endif
961#ifdef HAVE_LZMA
962 else if (lzma != NULL) {
963 delete lzma;
964 lzma = NULL;
965 }
966#endif
967 return true;
968 }
969 bool CloseDown(std::string const &FileName)
970 {
971 bool const Res = InternalClose(FileName);
972
973 if (compressor_pid > 0)
974 ExecWait(compressor_pid, "FileFdCompressor", true);
975 compressor_pid = -1;
976
977 return Res;
978 }
979 bool InternalStream() const {
980 return false
981#ifdef HAVE_BZ2
982 || bz2 != NULL
983#endif
984#ifdef HAVE_LZMA
985 || lzma != NULL
986#endif
987 ;
988 }
989
990
991 ~FileFdPrivate() { CloseDown(""); }
992};
993 /*}}}*/
13d87e2e 994// FileFd::Open - Open a file /*{{{*/
578bfd0a
AL
995// ---------------------------------------------------------------------
996/* The most commonly used open mode combinations are given with Mode */
e5f3f8c1 997bool FileFd::Open(string FileName,unsigned int const Mode,CompressMode Compress, unsigned long const AccessMode)
578bfd0a 998{
257e8d66 999 if (Mode == ReadOnlyGzip)
e5f3f8c1 1000 return Open(FileName, ReadOnly, Gzip, AccessMode);
257e8d66 1001
468720c5 1002 if (Compress == Auto && (Mode & WriteOnly) == WriteOnly)
ae635e3c 1003 return FileFdError("Autodetection on %s only works in ReadOnly openmode!", FileName.c_str());
257e8d66 1004
468720c5
DK
1005 std::vector<APT::Configuration::Compressor> const compressors = APT::Configuration::getCompressors();
1006 std::vector<APT::Configuration::Compressor>::const_iterator compressor = compressors.begin();
1007 if (Compress == Auto)
1008 {
468720c5
DK
1009 for (; compressor != compressors.end(); ++compressor)
1010 {
e788a834 1011 std::string file = FileName + compressor->Extension;
468720c5
DK
1012 if (FileExists(file) == false)
1013 continue;
1014 FileName = file;
468720c5
DK
1015 break;
1016 }
1017 }
1018 else if (Compress == Extension)
1019 {
52b47296
DK
1020 std::string::size_type const found = FileName.find_last_of('.');
1021 std::string ext;
1022 if (found != std::string::npos)
1023 {
1024 ext = FileName.substr(found);
1025 if (ext == ".new" || ext == ".bak")
1026 {
1027 std::string::size_type const found2 = FileName.find_last_of('.', found - 1);
1028 if (found2 != std::string::npos)
1029 ext = FileName.substr(found2, found - found2);
1030 else
1031 ext.clear();
1032 }
1033 }
aee1aac6
DK
1034 for (; compressor != compressors.end(); ++compressor)
1035 if (ext == compressor->Extension)
1036 break;
1037 // no matching extension - assume uncompressed (imagine files like 'example.org_Packages')
1038 if (compressor == compressors.end())
1039 for (compressor = compressors.begin(); compressor != compressors.end(); ++compressor)
1040 if (compressor->Name == ".")
468720c5 1041 break;
468720c5 1042 }
aee1aac6 1043 else
468720c5
DK
1044 {
1045 std::string name;
1046 switch (Compress)
1047 {
aee1aac6 1048 case None: name = "."; break;
468720c5
DK
1049 case Gzip: name = "gzip"; break;
1050 case Bzip2: name = "bzip2"; break;
1051 case Lzma: name = "lzma"; break;
1052 case Xz: name = "xz"; break;
aee1aac6
DK
1053 case Auto:
1054 case Extension:
52b47296 1055 // Unreachable
ae635e3c 1056 return FileFdError("Opening File %s in None, Auto or Extension should be already handled?!?", FileName.c_str());
468720c5
DK
1057 }
1058 for (; compressor != compressors.end(); ++compressor)
1059 if (compressor->Name == name)
1060 break;
aee1aac6 1061 if (compressor == compressors.end())
ae635e3c 1062 return FileFdError("Can't find a configured compressor %s for file %s", name.c_str(), FileName.c_str());
468720c5
DK
1063 }
1064
aee1aac6 1065 if (compressor == compressors.end())
ae635e3c 1066 return FileFdError("Can't find a match for specified compressor mode for file %s", FileName.c_str());
e5f3f8c1 1067 return Open(FileName, Mode, *compressor, AccessMode);
aee1aac6 1068}
e5f3f8c1 1069bool FileFd::Open(string FileName,unsigned int const Mode,APT::Configuration::Compressor const &compressor, unsigned long const AccessMode)
aee1aac6
DK
1070{
1071 Close();
aee1aac6
DK
1072 Flags = AutoClose;
1073
1074 if ((Mode & WriteOnly) != WriteOnly && (Mode & (Atomic | Create | Empty | Exclusive)) != 0)
ae635e3c 1075 return FileFdError("ReadOnly mode for %s doesn't accept additional flags!", FileName.c_str());
aee1aac6 1076 if ((Mode & ReadWrite) == 0)
ae635e3c 1077 return FileFdError("No openmode provided in FileFd::Open for %s", FileName.c_str());
468720c5 1078
257e8d66
DK
1079 if ((Mode & Atomic) == Atomic)
1080 {
1081 Flags |= Replace;
257e8d66
DK
1082 }
1083 else if ((Mode & (Exclusive | Create)) == (Exclusive | Create))
1084 {
1085 // for atomic, this will be done by rename in Close()
1086 unlink(FileName.c_str());
1087 }
1088 if ((Mode & Empty) == Empty)
578bfd0a 1089 {
257e8d66
DK
1090 struct stat Buf;
1091 if (lstat(FileName.c_str(),&Buf) == 0 && S_ISLNK(Buf.st_mode))
1092 unlink(FileName.c_str());
1093 }
c4fc2fd7 1094
561f860a
DK
1095 int fileflags = 0;
1096 #define if_FLAGGED_SET(FLAG, MODE) if ((Mode & FLAG) == FLAG) fileflags |= MODE
1097 if_FLAGGED_SET(ReadWrite, O_RDWR);
1098 else if_FLAGGED_SET(ReadOnly, O_RDONLY);
1099 else if_FLAGGED_SET(WriteOnly, O_WRONLY);
4a9db827 1100
561f860a
DK
1101 if_FLAGGED_SET(Create, O_CREAT);
1102 if_FLAGGED_SET(Empty, O_TRUNC);
1103 if_FLAGGED_SET(Exclusive, O_EXCL);
561f860a 1104 #undef if_FLAGGED_SET
52b47296 1105
7335eebe
AGM
1106 if ((Mode & Atomic) == Atomic)
1107 {
1108 char *name = strdup((FileName + ".XXXXXX").c_str());
1109
dc545c0b 1110 if((iFd = mkstemp(name)) == -1)
7335eebe
AGM
1111 {
1112 free(name);
98b69f9d 1113 return FileFdErrno("mkstemp", "Could not create temporary file for %s", FileName.c_str());
7335eebe
AGM
1114 }
1115
1116 TemporaryFileName = string(name);
7335eebe 1117 free(name);
dc545c0b 1118
230e69d7
DK
1119 // umask() will always set the umask and return the previous value, so
1120 // we first set the umask and then reset it to the old value
1121 mode_t const CurrentUmask = umask(0);
1122 umask(CurrentUmask);
1123 // calculate the actual file permissions (just like open/creat)
1124 mode_t const FilePermissions = (AccessMode & ~CurrentUmask);
1125
1126 if(fchmod(iFd, FilePermissions) == -1)
dc545c0b 1127 return FileFdErrno("fchmod", "Could not change permissions for temporary file %s", TemporaryFileName.c_str());
7335eebe 1128 }
468720c5 1129 else
230e69d7 1130 iFd = open(FileName.c_str(), fileflags, AccessMode);
468720c5 1131
b711c01e 1132 this->FileName = FileName;
561f860a
DK
1133 if (iFd == -1 || OpenInternDescriptor(Mode, compressor) == false)
1134 {
468720c5 1135 if (iFd != -1)
fc81e8f2 1136 {
561f860a
DK
1137 close (iFd);
1138 iFd = -1;
fc81e8f2 1139 }
ae635e3c 1140 return FileFdErrno("open",_("Could not open file %s"), FileName.c_str());
257e8d66 1141 }
578bfd0a 1142
13d87e2e
AL
1143 SetCloseExec(iFd,true);
1144 return true;
578bfd0a 1145}
257e8d66
DK
1146 /*}}}*/
1147// FileFd::OpenDescriptor - Open a filedescriptor /*{{{*/
1148// ---------------------------------------------------------------------
1149/* */
52b47296 1150bool FileFd::OpenDescriptor(int Fd, unsigned int const Mode, CompressMode Compress, bool AutoClose)
aee1aac6
DK
1151{
1152 std::vector<APT::Configuration::Compressor> const compressors = APT::Configuration::getCompressors();
1153 std::vector<APT::Configuration::Compressor>::const_iterator compressor = compressors.begin();
1154 std::string name;
bce778a3
MV
1155
1156 // compat with the old API
1157 if (Mode == ReadOnlyGzip && Compress == None)
1158 Compress = Gzip;
1159
aee1aac6
DK
1160 switch (Compress)
1161 {
1162 case None: name = "."; break;
1163 case Gzip: name = "gzip"; break;
1164 case Bzip2: name = "bzip2"; break;
1165 case Lzma: name = "lzma"; break;
1166 case Xz: name = "xz"; break;
1167 case Auto:
1168 case Extension:
f97bb523
DK
1169 if (AutoClose == true && Fd != -1)
1170 close(Fd);
ae635e3c 1171 return FileFdError("Opening Fd %d in Auto or Extension compression mode is not supported", Fd);
aee1aac6
DK
1172 }
1173 for (; compressor != compressors.end(); ++compressor)
1174 if (compressor->Name == name)
1175 break;
1176 if (compressor == compressors.end())
f97bb523
DK
1177 {
1178 if (AutoClose == true && Fd != -1)
1179 close(Fd);
ae635e3c 1180 return FileFdError("Can't find a configured compressor %s for file %s", name.c_str(), FileName.c_str());
f97bb523 1181 }
aee1aac6
DK
1182 return OpenDescriptor(Fd, Mode, *compressor, AutoClose);
1183}
52b47296 1184bool FileFd::OpenDescriptor(int Fd, unsigned int const Mode, APT::Configuration::Compressor const &compressor, bool AutoClose)
144c0969
JAK
1185{
1186 Close();
1187 Flags = (AutoClose) ? FileFd::AutoClose : 0;
84baaae9 1188 iFd = Fd;
b711c01e 1189 this->FileName = "";
84baaae9 1190 if (OpenInternDescriptor(Mode, compressor) == false)
468720c5 1191 {
f97bb523 1192 if (iFd != -1 && (
84baaae9 1193 (Flags & Compressed) == Compressed ||
f97bb523
DK
1194 AutoClose == true))
1195 {
468720c5 1196 close (iFd);
f97bb523
DK
1197 iFd = -1;
1198 }
1199 return FileFdError(_("Could not open file descriptor %d"), Fd);
144c0969 1200 }
144c0969 1201 return true;
468720c5 1202}
52b47296 1203bool FileFd::OpenInternDescriptor(unsigned int const Mode, APT::Configuration::Compressor const &compressor)
468720c5 1204{
84baaae9
DK
1205 if (iFd == -1)
1206 return false;
ff477ee1
DK
1207 if (compressor.Name == "." || compressor.Binary.empty() == true)
1208 return true;
1209
7f350a37 1210#if defined HAVE_ZLIB || defined HAVE_BZ2 || defined HAVE_LZMA
69d6988a
DK
1211 // the API to open files is similar, so setup to avoid code duplicates later
1212 // and while at it ensure that we close before opening (if its a reopen)
1213 void* (*compress_open)(int, const char *) = NULL;
7f350a37
DK
1214 if (false)
1215 /* dummy so that the rest can be 'else if's */;
4239dbca 1216#define APT_COMPRESS_INIT(NAME,OPEN) \
7f350a37 1217 else if (compressor.Name == NAME) \
69d6988a
DK
1218 { \
1219 compress_open = (void*(*)(int, const char *)) OPEN; \
4239dbca 1220 if (d != NULL) d->InternalClose(FileName); \
69d6988a
DK
1221 }
1222#ifdef HAVE_ZLIB
4239dbca 1223 APT_COMPRESS_INIT("gzip", gzdopen)
69d6988a
DK
1224#endif
1225#ifdef HAVE_BZ2
4239dbca 1226 APT_COMPRESS_INIT("bzip2", BZ2_bzdopen)
69d6988a 1227#endif
7f350a37 1228#ifdef HAVE_LZMA
4239dbca
DK
1229 APT_COMPRESS_INIT("xz", fdopen)
1230 APT_COMPRESS_INIT("lzma", fdopen)
7f350a37 1231#endif
69d6988a
DK
1232#undef APT_COMPRESS_INIT
1233#endif
1234
ba667cf7
DK
1235 if (d == NULL)
1236 {
1237 d = new FileFdPrivate();
1238 d->openmode = Mode;
1239 d->compressor = compressor;
7f350a37 1240#if defined HAVE_ZLIB || defined HAVE_BZ2 || defined HAVE_LZMA
f6ffe501 1241 if ((Flags & AutoClose) != AutoClose && compress_open != NULL)
84baaae9
DK
1242 {
1243 // Need to duplicate fd here or gz/bz2 close for cleanup will close the fd as well
1244 int const internFd = dup(iFd);
1245 if (internFd == -1)
1246 return FileFdErrno("OpenInternDescriptor", _("Could not open file descriptor %d"), iFd);
1247 iFd = internFd;
1248 }
69d6988a 1249#endif
ba667cf7 1250 }
ff477ee1 1251
7f350a37 1252#if defined HAVE_ZLIB || defined HAVE_BZ2 || defined HAVE_LZMA
69d6988a 1253 if (compress_open != NULL)
468720c5 1254 {
69d6988a 1255 void* compress_struct = NULL;
468720c5 1256 if ((Mode & ReadWrite) == ReadWrite)
69d6988a 1257 compress_struct = compress_open(iFd, "r+");
468720c5 1258 else if ((Mode & WriteOnly) == WriteOnly)
69d6988a 1259 compress_struct = compress_open(iFd, "w");
468720c5 1260 else
69d6988a
DK
1261 compress_struct = compress_open(iFd, "r");
1262 if (compress_struct == NULL)
468720c5 1263 return false;
69d6988a 1264
7f350a37
DK
1265 if (false)
1266 /* dummy so that the rest can be 'else if's */;
69d6988a 1267#ifdef HAVE_ZLIB
7f350a37 1268 else if (compressor.Name == "gzip")
69d6988a 1269 d->gz = (gzFile) compress_struct;
699b209e 1270#endif
c4997486 1271#ifdef HAVE_BZ2
7f350a37 1272 else if (compressor.Name == "bzip2")
69d6988a 1273 d->bz2 = (BZFILE*) compress_struct;
7f350a37
DK
1274#endif
1275#ifdef HAVE_LZMA
1276 else if (compressor.Name == "xz" || compressor.Name == "lzma")
adbd7eb4 1277 {
7f350a37
DK
1278 uint32_t const xzlevel = 6;
1279 uint64_t const memlimit = UINT64_MAX;
1280 if (d->lzma == NULL)
1281 d->lzma = new FileFdPrivate::LZMAFILE;
1282 d->lzma->file = (FILE*) compress_struct;
21ea1dbb
MV
1283 lzma_stream tmp_stream = LZMA_STREAM_INIT;
1284 d->lzma->stream = tmp_stream;
7f350a37
DK
1285
1286 if ((Mode & ReadWrite) == ReadWrite)
1287 return FileFdError("ReadWrite mode is not supported for file %s", FileName.c_str());
1288
1289 if ((Mode & WriteOnly) == WriteOnly)
1290 {
1291 if (compressor.Name == "xz")
1292 {
1293 if (lzma_easy_encoder(&d->lzma->stream, xzlevel, LZMA_CHECK_CRC32) != LZMA_OK)
1294 return false;
1295 }
1296 else
1297 {
1298 lzma_options_lzma options;
1299 lzma_lzma_preset(&options, xzlevel);
1300 if (lzma_alone_encoder(&d->lzma->stream, &options) != LZMA_OK)
1301 return false;
1302 }
1303 d->lzma->compressing = true;
1304 }
1305 else
1306 {
1307 if (compressor.Name == "xz")
1308 {
1309 if (lzma_auto_decoder(&d->lzma->stream, memlimit, 0) != LZMA_OK)
1310 return false;
1311 }
1312 else
1313 {
1314 if (lzma_alone_decoder(&d->lzma->stream, memlimit) != LZMA_OK)
1315 return false;
1316 }
1317 d->lzma->compressing = false;
1318 }
adbd7eb4 1319 }
69d6988a 1320#endif
c4997486
DK
1321 Flags |= Compressed;
1322 return true;
1323 }
1324#endif
1325
adbd7eb4
DK
1326 // collect zombies here in case we reopen
1327 if (d->compressor_pid > 0)
1328 ExecWait(d->compressor_pid, "FileFdCompressor", true);
561f860a
DK
1329
1330 if ((Mode & ReadWrite) == ReadWrite)
ae635e3c 1331 return FileFdError("ReadWrite mode is not supported for file %s", FileName.c_str());
561f860a
DK
1332
1333 bool const Comp = (Mode & WriteOnly) == WriteOnly;
561f860a
DK
1334 if (Comp == false)
1335 {
adbd7eb4 1336 // Handle 'decompression' of empty files
561f860a
DK
1337 struct stat Buf;
1338 fstat(iFd, &Buf);
1339 if (Buf.st_size == 0 && S_ISFIFO(Buf.st_mode) == false)
1340 return true;
1341
1342 // We don't need the file open - instead let the compressor open it
1343 // as he properly knows better how to efficiently read from 'his' file
1344 if (FileName.empty() == false)
afb093cd 1345 {
561f860a 1346 close(iFd);
afb093cd
DK
1347 iFd = -1;
1348 }
561f860a
DK
1349 }
1350
1351 // Create a data pipe
1352 int Pipe[2] = {-1,-1};
1353 if (pipe(Pipe) != 0)
ae635e3c 1354 return FileFdErrno("pipe",_("Failed to create subprocess IPC"));
561f860a
DK
1355 for (int J = 0; J != 2; J++)
1356 SetCloseExec(Pipe[J],true);
1357
1358 d->compressed_fd = iFd;
1359 d->pipe = true;
1360
1361 if (Comp == true)
1362 iFd = Pipe[1];
1363 else
1364 iFd = Pipe[0];
1365
1366 // The child..
1367 d->compressor_pid = ExecFork();
1368 if (d->compressor_pid == 0)
1369 {
1370 if (Comp == true)
1371 {
1372 dup2(d->compressed_fd,STDOUT_FILENO);
1373 dup2(Pipe[0],STDIN_FILENO);
1374 }
1375 else
1376 {
7f350a37 1377 if (d->compressed_fd != -1)
561f860a
DK
1378 dup2(d->compressed_fd,STDIN_FILENO);
1379 dup2(Pipe[1],STDOUT_FILENO);
1380 }
0b4895d3
DK
1381 int const nullfd = open("/dev/null", O_WRONLY);
1382 if (nullfd != -1)
1383 {
1384 dup2(nullfd,STDERR_FILENO);
1385 close(nullfd);
1386 }
561f860a
DK
1387
1388 SetCloseExec(STDOUT_FILENO,false);
1389 SetCloseExec(STDIN_FILENO,false);
1390
1391 std::vector<char const*> Args;
1392 Args.push_back(compressor.Binary.c_str());
1393 std::vector<std::string> const * const addArgs =
1394 (Comp == true) ? &(compressor.CompressArgs) : &(compressor.UncompressArgs);
1395 for (std::vector<std::string>::const_iterator a = addArgs->begin();
1396 a != addArgs->end(); ++a)
1397 Args.push_back(a->c_str());
1398 if (Comp == false && FileName.empty() == false)
1399 {
bb93178b
DK
1400 // commands not needing arguments, do not need to be told about using standard output
1401 // in reality, only testcases with tools like cat, rev, rot13, … are able to trigger this
1402 if (compressor.CompressArgs.empty() == false && compressor.UncompressArgs.empty() == false)
1403 Args.push_back("--stdout");
561f860a
DK
1404 if (TemporaryFileName.empty() == false)
1405 Args.push_back(TemporaryFileName.c_str());
1406 else
1407 Args.push_back(FileName.c_str());
1408 }
1409 Args.push_back(NULL);
1410
1411 execvp(Args[0],(char **)&Args[0]);
1412 cerr << _("Failed to exec compressor ") << Args[0] << endl;
1413 _exit(100);
1414 }
1415 if (Comp == true)
1416 close(Pipe[0]);
468720c5 1417 else
561f860a 1418 close(Pipe[1]);
561f860a 1419
468720c5 1420 return true;
144c0969 1421}
578bfd0a 1422 /*}}}*/
8e06abb2 1423// FileFd::~File - Closes the file /*{{{*/
578bfd0a
AL
1424// ---------------------------------------------------------------------
1425/* If the proper modes are selected then we close the Fd and possibly
1426 unlink the file on error. */
8e06abb2 1427FileFd::~FileFd()
578bfd0a
AL
1428{
1429 Close();
500400fe 1430 if (d != NULL)
500400fe 1431 d->CloseDown(FileName);
96ab3c6f
MV
1432 delete d;
1433 d = NULL;
578bfd0a
AL
1434}
1435 /*}}}*/
8e06abb2 1436// FileFd::Read - Read a bit of the file /*{{{*/
578bfd0a 1437// ---------------------------------------------------------------------
1e3f4083 1438/* We are careful to handle interruption by a signal while reading
b0db36b1 1439 gracefully. */
650faab0 1440bool FileFd::Read(void *To,unsigned long long Size,unsigned long long *Actual)
578bfd0a 1441{
3286ad13 1442 ssize_t Res;
b0db36b1 1443 errno = 0;
f604cf55
AL
1444 if (Actual != 0)
1445 *Actual = 0;
699b209e 1446 *((char *)To) = '\0';
b0db36b1 1447 do
578bfd0a 1448 {
7f350a37
DK
1449 if (false)
1450 /* dummy so that the rest can be 'else if's */;
7efb8c8e 1451#ifdef HAVE_ZLIB
7f350a37 1452 else if (d != NULL && d->gz != NULL)
c4997486 1453 Res = gzread(d->gz,To,Size);
c4997486
DK
1454#endif
1455#ifdef HAVE_BZ2
7f350a37 1456 else if (d != NULL && d->bz2 != NULL)
c4997486 1457 Res = BZ2_bzread(d->bz2,To,Size);
699b209e 1458#endif
7f350a37
DK
1459#ifdef HAVE_LZMA
1460 else if (d != NULL && d->lzma != NULL)
1461 {
1462 if (d->lzma->eof == true)
1463 break;
1464
1465 d->lzma->stream.next_out = (uint8_t *) To;
1466 d->lzma->stream.avail_out = Size;
1467 if (d->lzma->stream.avail_in == 0)
1468 {
1469 d->lzma->stream.next_in = d->lzma->buffer;
1470 d->lzma->stream.avail_in = fread(d->lzma->buffer, 1, sizeof(d->lzma->buffer)/sizeof(d->lzma->buffer[0]), d->lzma->file);
1471 }
1472 d->lzma->err = lzma_code(&d->lzma->stream, LZMA_RUN);
1473 if (d->lzma->err == LZMA_STREAM_END)
1474 {
1475 d->lzma->eof = true;
1476 Res = Size - d->lzma->stream.avail_out;
1477 }
1478 else if (d->lzma->err != LZMA_OK)
1479 {
1480 Res = -1;
1481 errno = 0;
1482 }
1483 else
c4b113e6 1484 {
7f350a37 1485 Res = Size - d->lzma->stream.avail_out;
c4b113e6
DK
1486 if (Res == 0)
1487 {
1488 // lzma run was okay, but produced no output…
1489 Res = -1;
1490 errno = EINTR;
1491 }
1492 }
7f350a37
DK
1493 }
1494#endif
1495 else
a3a03f5d 1496 Res = read(iFd,To,Size);
b711c01e 1497
b0db36b1
AL
1498 if (Res < 0)
1499 {
b711c01e 1500 if (errno == EINTR)
c4b113e6
DK
1501 {
1502 // trick the while-loop into running again
1503 Res = 1;
1504 errno = 0;
b711c01e 1505 continue;
c4b113e6 1506 }
7f350a37
DK
1507 if (false)
1508 /* dummy so that the rest can be 'else if's */;
7efb8c8e 1509#ifdef HAVE_ZLIB
7f350a37 1510 else if (d != NULL && d->gz != NULL)
b711c01e
DK
1511 {
1512 int err;
1513 char const * const errmsg = gzerror(d->gz, &err);
1514 if (err != Z_ERRNO)
ae635e3c 1515 return FileFdError("gzread: %s (%d: %s)", _("Read error"), err, errmsg);
b711c01e 1516 }
c4997486
DK
1517#endif
1518#ifdef HAVE_BZ2
7f350a37 1519 else if (d != NULL && d->bz2 != NULL)
c4997486
DK
1520 {
1521 int err;
1522 char const * const errmsg = BZ2_bzerror(d->bz2, &err);
1523 if (err != BZ_IO_ERROR)
c36db2b5 1524 return FileFdError("BZ2_bzread: %s %s (%d: %s)", FileName.c_str(), _("Read error"), err, errmsg);
c4997486 1525 }
7f350a37
DK
1526#endif
1527#ifdef HAVE_LZMA
1528 else if (d != NULL && d->lzma != NULL)
1529 return FileFdError("lzma_read: %s (%d)", _("Read error"), d->lzma->err);
b711c01e 1530#endif
ae635e3c 1531 return FileFdErrno("read",_("Read error"));
b0db36b1 1532 }
578bfd0a 1533
b0db36b1
AL
1534 To = (char *)To + Res;
1535 Size -= Res;
ff477ee1
DK
1536 if (d != NULL)
1537 d->seekpos += Res;
f604cf55
AL
1538 if (Actual != 0)
1539 *Actual += Res;
b0db36b1
AL
1540 }
1541 while (Res > 0 && Size > 0);
1542
1543 if (Size == 0)
1544 return true;
1545
ddc1d8d0 1546 // Eof handling
f604cf55 1547 if (Actual != 0)
ddc1d8d0
AL
1548 {
1549 Flags |= HitEof;
1550 return true;
1551 }
ae635e3c
DK
1552
1553 return FileFdError(_("read, still have %llu to read but none left"), Size);
578bfd0a
AL
1554}
1555 /*}}}*/
032bd56f
DK
1556// FileFd::ReadLine - Read a complete line from the file /*{{{*/
1557// ---------------------------------------------------------------------
1558/* Beware: This method can be quiet slow for big buffers on UNcompressed
1559 files because of the naive implementation! */
1560char* FileFd::ReadLine(char *To, unsigned long long const Size)
1561{
699b209e 1562 *To = '\0';
7efb8c8e 1563#ifdef HAVE_ZLIB
ff477ee1 1564 if (d != NULL && d->gz != NULL)
032bd56f 1565 return gzgets(d->gz, To, Size);
699b209e 1566#endif
032bd56f
DK
1567
1568 unsigned long long read = 0;
40468850
DK
1569 while ((Size - 1) != read)
1570 {
1571 unsigned long long done = 0;
1572 if (Read(To + read, 1, &done) == false)
1573 return NULL;
1574 if (done == 0)
1575 break;
1576 if (To[read++] == '\n')
1577 break;
1578 }
1579 if (read == 0)
032bd56f 1580 return NULL;
40468850 1581 To[read] = '\0';
032bd56f
DK
1582 return To;
1583}
1584 /*}}}*/
8e06abb2 1585// FileFd::Write - Write to the file /*{{{*/
578bfd0a
AL
1586// ---------------------------------------------------------------------
1587/* */
650faab0 1588bool FileFd::Write(const void *From,unsigned long long Size)
578bfd0a 1589{
3286ad13 1590 ssize_t Res;
b0db36b1
AL
1591 errno = 0;
1592 do
578bfd0a 1593 {
7f350a37
DK
1594 if (false)
1595 /* dummy so that the rest can be 'else if's */;
7efb8c8e 1596#ifdef HAVE_ZLIB
7f350a37
DK
1597 else if (d != NULL && d->gz != NULL)
1598 Res = gzwrite(d->gz,From,Size);
c4997486
DK
1599#endif
1600#ifdef HAVE_BZ2
7f350a37
DK
1601 else if (d != NULL && d->bz2 != NULL)
1602 Res = BZ2_bzwrite(d->bz2,(void*)From,Size);
699b209e 1603#endif
7f350a37
DK
1604#ifdef HAVE_LZMA
1605 else if (d != NULL && d->lzma != NULL)
1606 {
1607 d->lzma->stream.next_in = (uint8_t *)From;
1608 d->lzma->stream.avail_in = Size;
1609 d->lzma->stream.next_out = d->lzma->buffer;
1610 d->lzma->stream.avail_out = sizeof(d->lzma->buffer)/sizeof(d->lzma->buffer[0]);
1611 d->lzma->err = lzma_code(&d->lzma->stream, LZMA_RUN);
1612 if (d->lzma->err != LZMA_OK)
1613 return false;
1614 size_t const n = sizeof(d->lzma->buffer)/sizeof(d->lzma->buffer[0]) - d->lzma->stream.avail_out;
1615 size_t const m = (n == 0) ? 0 : fwrite(d->lzma->buffer, 1, n, d->lzma->file);
1616 if (m != n)
1617 Res = -1;
1618 else
1619 Res = Size - d->lzma->stream.avail_in;
1620 }
699b209e 1621#endif
7f350a37
DK
1622 else
1623 Res = write(iFd,From,Size);
1624
b0db36b1
AL
1625 if (Res < 0 && errno == EINTR)
1626 continue;
1627 if (Res < 0)
1628 {
7f350a37
DK
1629 if (false)
1630 /* dummy so that the rest can be 'else if's */;
c4997486 1631#ifdef HAVE_ZLIB
7f350a37 1632 else if (d != NULL && d->gz != NULL)
c4997486
DK
1633 {
1634 int err;
1635 char const * const errmsg = gzerror(d->gz, &err);
1636 if (err != Z_ERRNO)
ae635e3c 1637 return FileFdError("gzwrite: %s (%d: %s)", _("Write error"), err, errmsg);
c4997486
DK
1638 }
1639#endif
1640#ifdef HAVE_BZ2
7f350a37 1641 else if (d != NULL && d->bz2 != NULL)
c4997486
DK
1642 {
1643 int err;
1644 char const * const errmsg = BZ2_bzerror(d->bz2, &err);
1645 if (err != BZ_IO_ERROR)
ae635e3c 1646 return FileFdError("BZ2_bzwrite: %s (%d: %s)", _("Write error"), err, errmsg);
c4997486 1647 }
7f350a37
DK
1648#endif
1649#ifdef HAVE_LZMA
1650 else if (d != NULL && d->lzma != NULL)
1651 return FileFdErrno("lzma_fwrite", _("Write error"));
c4997486 1652#endif
ae635e3c 1653 return FileFdErrno("write",_("Write error"));
b0db36b1
AL
1654 }
1655
cf4ff3b7 1656 From = (char const *)From + Res;
b0db36b1 1657 Size -= Res;
ff477ee1
DK
1658 if (d != NULL)
1659 d->seekpos += Res;
578bfd0a 1660 }
b0db36b1 1661 while (Res > 0 && Size > 0);
578bfd0a 1662
b0db36b1
AL
1663 if (Size == 0)
1664 return true;
ae635e3c
DK
1665
1666 return FileFdError(_("write, still have %llu to write but couldn't"), Size);
d68d65ad
DK
1667}
1668bool FileFd::Write(int Fd, const void *From, unsigned long long Size)
1669{
3286ad13 1670 ssize_t Res;
d68d65ad
DK
1671 errno = 0;
1672 do
1673 {
1674 Res = write(Fd,From,Size);
1675 if (Res < 0 && errno == EINTR)
1676 continue;
1677 if (Res < 0)
1678 return _error->Errno("write",_("Write error"));
1679
cf4ff3b7 1680 From = (char const *)From + Res;
d68d65ad
DK
1681 Size -= Res;
1682 }
1683 while (Res > 0 && Size > 0);
1684
1685 if (Size == 0)
1686 return true;
1687
1688 return _error->Error(_("write, still have %llu to write but couldn't"), Size);
578bfd0a
AL
1689}
1690 /*}}}*/
8e06abb2 1691// FileFd::Seek - Seek in the file /*{{{*/
578bfd0a
AL
1692// ---------------------------------------------------------------------
1693/* */
650faab0 1694bool FileFd::Seek(unsigned long long To)
578bfd0a 1695{
bb93178b
DK
1696 Flags &= ~HitEof;
1697
4239dbca 1698 if (d != NULL && (d->pipe == true || d->InternalStream() == true))
699b209e 1699 {
1abbc47c
DK
1700 // Our poor man seeking in pipes is costly, so try to avoid it
1701 unsigned long long seekpos = Tell();
1702 if (seekpos == To)
1703 return true;
1704 else if (seekpos < To)
1705 return Skip(To - seekpos);
1706
561f860a 1707 if ((d->openmode & ReadOnly) != ReadOnly)
ae635e3c 1708 return FileFdError("Reopen is only implemented for read-only files!");
4239dbca 1709 d->InternalClose(FileName);
afb093cd
DK
1710 if (iFd != -1)
1711 close(iFd);
1712 iFd = -1;
561f860a
DK
1713 if (TemporaryFileName.empty() == false)
1714 iFd = open(TemporaryFileName.c_str(), O_RDONLY);
1715 else if (FileName.empty() == false)
1716 iFd = open(FileName.c_str(), O_RDONLY);
1717 else
6fd947bd
DK
1718 {
1719 if (d->compressed_fd > 0)
1720 if (lseek(d->compressed_fd, 0, SEEK_SET) != 0)
1721 iFd = d->compressed_fd;
500400fe 1722 if (iFd < 0)
ae635e3c 1723 return FileFdError("Reopen is not implemented for pipes opened with FileFd::OpenDescriptor()!");
6fd947bd 1724 }
561f860a
DK
1725
1726 if (OpenInternDescriptor(d->openmode, d->compressor) == false)
ae635e3c 1727 return FileFdError("Seek on file %s because it couldn't be reopened", FileName.c_str());
561f860a
DK
1728
1729 if (To != 0)
1730 return Skip(To);
1abbc47c
DK
1731
1732 d->seekpos = To;
561f860a 1733 return true;
699b209e 1734 }
3286ad13 1735 off_t res;
7efb8c8e 1736#ifdef HAVE_ZLIB
ff477ee1 1737 if (d != NULL && d->gz)
032bd56f 1738 res = gzseek(d->gz,To,SEEK_SET);
a3a03f5d 1739 else
699b209e 1740#endif
a3a03f5d 1741 res = lseek(iFd,To,SEEK_SET);
3286ad13 1742 if (res != (off_t)To)
ae635e3c 1743 return FileFdError("Unable to seek to %llu", To);
1abbc47c 1744
ff477ee1
DK
1745 if (d != NULL)
1746 d->seekpos = To;
727f18af
AL
1747 return true;
1748}
1749 /*}}}*/
1750// FileFd::Skip - Seek in the file /*{{{*/
1751// ---------------------------------------------------------------------
1752/* */
650faab0 1753bool FileFd::Skip(unsigned long long Over)
727f18af 1754{
4239dbca 1755 if (d != NULL && (d->pipe == true || d->InternalStream() == true))
40468850 1756 {
40468850
DK
1757 char buffer[1024];
1758 while (Over != 0)
1759 {
1760 unsigned long long toread = std::min((unsigned long long) sizeof(buffer), Over);
1761 if (Read(buffer, toread) == false)
ae635e3c 1762 return FileFdError("Unable to seek ahead %llu",Over);
40468850
DK
1763 Over -= toread;
1764 }
1765 return true;
1766 }
1767
3286ad13 1768 off_t res;
7efb8c8e 1769#ifdef HAVE_ZLIB
ff477ee1 1770 if (d != NULL && d->gz != NULL)
032bd56f 1771 res = gzseek(d->gz,Over,SEEK_CUR);
a3a03f5d 1772 else
699b209e 1773#endif
a3a03f5d 1774 res = lseek(iFd,Over,SEEK_CUR);
1775 if (res < 0)
ae635e3c 1776 return FileFdError("Unable to seek ahead %llu",Over);
ff477ee1
DK
1777 if (d != NULL)
1778 d->seekpos = res;
1abbc47c 1779
6d5dd02a
AL
1780 return true;
1781}
1782 /*}}}*/
1783// FileFd::Truncate - Truncate the file /*{{{*/
1784// ---------------------------------------------------------------------
1785/* */
650faab0 1786bool FileFd::Truncate(unsigned long long To)
6d5dd02a 1787{
ad5051ef
DK
1788 // truncating /dev/null is always successful - as we get an error otherwise
1789 if (To == 0 && FileName == "/dev/null")
1790 return true;
7f350a37 1791#if defined HAVE_ZLIB || defined HAVE_BZ2 || defined HAVE_LZMA
4239dbca 1792 if (d != NULL && (d->InternalStream() == true
7f350a37 1793#ifdef HAVE_ZLIB
4239dbca 1794 || d->gz != NULL
7f350a37 1795#endif
4239dbca 1796 ))
ae635e3c 1797 return FileFdError("Truncating compressed files is not implemented (%s)", FileName.c_str());
c4997486 1798#endif
6d5dd02a 1799 if (ftruncate(iFd,To) != 0)
ae635e3c
DK
1800 return FileFdError("Unable to truncate to %llu",To);
1801
578bfd0a
AL
1802 return true;
1803}
1804 /*}}}*/
7f25bdff
AL
1805// FileFd::Tell - Current seek position /*{{{*/
1806// ---------------------------------------------------------------------
1807/* */
650faab0 1808unsigned long long FileFd::Tell()
7f25bdff 1809{
1abbc47c
DK
1810 // In theory, we could just return seekpos here always instead of
1811 // seeking around, but not all users of FileFd use always Seek() and co
1812 // so d->seekpos isn't always true and we can just use it as a hint if
1813 // we have nothing else, but not always as an authority…
4239dbca 1814 if (d != NULL && (d->pipe == true || d->InternalStream() == true))
1abbc47c
DK
1815 return d->seekpos;
1816
a3a03f5d 1817 off_t Res;
7efb8c8e 1818#ifdef HAVE_ZLIB
ff477ee1 1819 if (d != NULL && d->gz != NULL)
032bd56f 1820 Res = gztell(d->gz);
a3a03f5d 1821 else
699b209e 1822#endif
a3a03f5d 1823 Res = lseek(iFd,0,SEEK_CUR);
7f25bdff 1824 if (Res == (off_t)-1)
ae635e3c 1825 FileFdErrno("lseek","Failed to determine the current file position");
ff477ee1
DK
1826 if (d != NULL)
1827 d->seekpos = Res;
7f25bdff
AL
1828 return Res;
1829}
1830 /*}}}*/
8190b07a 1831static bool StatFileFd(char const * const msg, int const iFd, std::string const &FileName, struct stat &Buf, FileFdPrivate * const d) /*{{{*/
578bfd0a 1832{
6008b79a
DK
1833 bool ispipe = (d != NULL && d->pipe == true);
1834 if (ispipe == false)
1835 {
1836 if (fstat(iFd,&Buf) != 0)
8190b07a
DK
1837 // higher-level code will generate more meaningful messages,
1838 // even translated this would be meaningless for users
1839 return _error->Errno("fstat", "Unable to determine %s for fd %i", msg, iFd);
003c40d3
DK
1840 if (FileName.empty() == false)
1841 ispipe = S_ISFIFO(Buf.st_mode);
6008b79a 1842 }
699b209e
DK
1843
1844 // for compressor pipes st_size is undefined and at 'best' zero
6008b79a 1845 if (ispipe == true)
699b209e
DK
1846 {
1847 // we set it here, too, as we get the info here for free
1848 // in theory the Open-methods should take care of it already
ff477ee1
DK
1849 if (d != NULL)
1850 d->pipe = true;
699b209e 1851 if (stat(FileName.c_str(), &Buf) != 0)
8190b07a
DK
1852 return _error->Errno("fstat", "Unable to determine %s for file %s", msg, FileName.c_str());
1853 }
1854 return true;
1855}
1856 /*}}}*/
1857// FileFd::FileSize - Return the size of the file /*{{{*/
1858unsigned long long FileFd::FileSize()
1859{
1860 struct stat Buf;
1861 if (StatFileFd("file size", iFd, FileName, Buf, d) == false)
1862 {
1863 Flags |= Fail;
1864 return 0;
699b209e 1865 }
4260fd39
DK
1866 return Buf.st_size;
1867}
1868 /*}}}*/
8190b07a
DK
1869// FileFd::ModificationTime - Return the time of last touch /*{{{*/
1870time_t FileFd::ModificationTime()
1871{
1872 struct stat Buf;
1873 if (StatFileFd("modification time", iFd, FileName, Buf, d) == false)
1874 {
1875 Flags |= Fail;
1876 return 0;
1877 }
1878 return Buf.st_mtime;
1879}
1880 /*}}}*/
4260fd39
DK
1881// FileFd::Size - Return the size of the content in the file /*{{{*/
1882// ---------------------------------------------------------------------
1883/* */
650faab0 1884unsigned long long FileFd::Size()
4260fd39 1885{
650faab0 1886 unsigned long long size = FileSize();
44dc669e 1887
699b209e
DK
1888 // for compressor pipes st_size is undefined and at 'best' zero,
1889 // so we 'read' the content and 'seek' back - see there
4239dbca 1890 if (d != NULL && (d->pipe == true || (d->InternalStream() == true && size > 0)))
699b209e 1891 {
1abbc47c 1892 unsigned long long const oldSeek = Tell();
699b209e
DK
1893 char ignore[1000];
1894 unsigned long long read = 0;
1895 do {
638593bd
DK
1896 if (Read(ignore, sizeof(ignore), &read) == false)
1897 {
1898 Seek(oldSeek);
1899 return 0;
1900 }
699b209e 1901 } while(read != 0);
1abbc47c
DK
1902 size = Tell();
1903 Seek(oldSeek);
699b209e 1904 }
7efb8c8e 1905#ifdef HAVE_ZLIB
44dc669e 1906 // only check gzsize if we are actually a gzip file, just checking for
032bd56f 1907 // "gz" is not sufficient as uncompressed files could be opened with
44dc669e 1908 // gzopen in "direct" mode as well
ff477ee1 1909 else if (d != NULL && d->gz && !gzdirect(d->gz) && size > 0)
9c182afa 1910 {
65c72a4b 1911 off_t const oldPos = lseek(iFd,0,SEEK_CUR);
9c182afa
MP
1912 /* unfortunately zlib.h doesn't provide a gzsize(), so we have to do
1913 * this ourselves; the original (uncompressed) file size is the last 32
1914 * bits of the file */
650faab0 1915 // FIXME: Size for gz-files is limited by 32bit… no largefile support
9c182afa 1916 if (lseek(iFd, -4, SEEK_END) < 0)
fbb89d94 1917 {
638593bd
DK
1918 FileFdErrno("lseek","Unable to seek to end of gzipped file");
1919 return 0;
fbb89d94 1920 }
05eab8af 1921 uint32_t size = 0;
9c182afa 1922 if (read(iFd, &size, 4) != 4)
fbb89d94 1923 {
638593bd
DK
1924 FileFdErrno("read","Unable to read original size of gzipped file");
1925 return 0;
fbb89d94 1926 }
05eab8af 1927 size = le32toh(size);
9c182afa 1928
65c72a4b 1929 if (lseek(iFd, oldPos, SEEK_SET) < 0)
fbb89d94 1930 {
638593bd
DK
1931 FileFdErrno("lseek","Unable to seek in gzipped file");
1932 return 0;
fbb89d94 1933 }
65c72a4b 1934
9c182afa
MP
1935 return size;
1936 }
699b209e 1937#endif
9c182afa 1938
44dc669e 1939 return size;
578bfd0a
AL
1940}
1941 /*}}}*/
8e06abb2 1942// FileFd::Close - Close the file if the close flag is set /*{{{*/
578bfd0a
AL
1943// ---------------------------------------------------------------------
1944/* */
8e06abb2 1945bool FileFd::Close()
578bfd0a 1946{
032bd56f
DK
1947 if (iFd == -1)
1948 return true;
1949
578bfd0a
AL
1950 bool Res = true;
1951 if ((Flags & AutoClose) == AutoClose)
d13c2d3f 1952 {
500400fe
DK
1953 if ((Flags & Compressed) != Compressed && iFd > 0 && close(iFd) != 0)
1954 Res &= _error->Errno("close",_("Problem closing the file %s"), FileName.c_str());
500400fe
DK
1955 if (d != NULL)
1956 {
1957 Res &= d->CloseDown(FileName);
1958 delete d;
1959 d = NULL;
1960 }
d13c2d3f 1961 }
3010fb0e 1962
d3aac32e 1963 if ((Flags & Replace) == Replace) {
3010fb0e 1964 if (rename(TemporaryFileName.c_str(), FileName.c_str()) != 0)
62d073d9
DK
1965 Res &= _error->Errno("rename",_("Problem renaming the file %s to %s"), TemporaryFileName.c_str(), FileName.c_str());
1966
fd3b761e 1967 FileName = TemporaryFileName; // for the unlink() below.
257e8d66 1968 TemporaryFileName.clear();
3010fb0e 1969 }
62d073d9
DK
1970
1971 iFd = -1;
1972
578bfd0a
AL
1973 if ((Flags & Fail) == Fail && (Flags & DelOnFail) == DelOnFail &&
1974 FileName.empty() == false)
1975 if (unlink(FileName.c_str()) != 0)
62d073d9 1976 Res &= _error->WarningE("unlnk",_("Problem unlinking the file %s"), FileName.c_str());
3010fb0e 1977
fbb89d94
DK
1978 if (Res == false)
1979 Flags |= Fail;
578bfd0a
AL
1980 return Res;
1981}
1982 /*}}}*/
b2e465d6
AL
1983// FileFd::Sync - Sync the file /*{{{*/
1984// ---------------------------------------------------------------------
1985/* */
1986bool FileFd::Sync()
1987{
b2e465d6 1988 if (fsync(iFd) != 0)
ae635e3c
DK
1989 return FileFdErrno("sync",_("Problem syncing the file"));
1990 return true;
1991}
1992 /*}}}*/
1993// FileFd::FileFdErrno - set Fail and call _error->Errno *{{{*/
1994bool FileFd::FileFdErrno(const char *Function, const char *Description,...)
1995{
1996 Flags |= Fail;
1997 va_list args;
1998 size_t msgSize = 400;
1999 int const errsv = errno;
2000 while (true)
fbb89d94 2001 {
ae635e3c
DK
2002 va_start(args,Description);
2003 if (_error->InsertErrno(GlobalError::ERROR, Function, Description, args, errsv, msgSize) == false)
2004 break;
2005 va_end(args);
fbb89d94 2006 }
ae635e3c
DK
2007 return false;
2008}
2009 /*}}}*/
2010// FileFd::FileFdError - set Fail and call _error->Error *{{{*/
2011bool FileFd::FileFdError(const char *Description,...) {
2012 Flags |= Fail;
2013 va_list args;
2014 size_t msgSize = 400;
2015 while (true)
2016 {
2017 va_start(args,Description);
2018 if (_error->Insert(GlobalError::ERROR, Description, args, msgSize) == false)
2019 break;
2020 va_end(args);
2021 }
2022 return false;
b2e465d6
AL
2023}
2024 /*}}}*/
699b209e 2025
7f350a37
DK
2026APT_DEPRECATED gzFile FileFd::gzFd() {
2027#ifdef HAVE_ZLIB
2028 return d->gz;
2029#else
2030 return NULL;
2031#endif
2032}
8d01b9d6 2033
f8aba23f 2034// Glob - wrapper around "glob()" /*{{{*/
8d01b9d6
MV
2035std::vector<std::string> Glob(std::string const &pattern, int flags)
2036{
2037 std::vector<std::string> result;
2038 glob_t globbuf;
ec4835a1
ÁGM
2039 int glob_res;
2040 unsigned int i;
8d01b9d6
MV
2041
2042 glob_res = glob(pattern.c_str(), flags, NULL, &globbuf);
2043
2044 if (glob_res != 0)
2045 {
2046 if(glob_res != GLOB_NOMATCH) {
2047 _error->Errno("glob", "Problem with glob");
2048 return result;
2049 }
2050 }
2051
2052 // append results
2053 for(i=0;i<globbuf.gl_pathc;i++)
2054 result.push_back(string(globbuf.gl_pathv[i]));
2055
2056 globfree(&globbuf);
2057 return result;
2058}
2059 /*}}}*/
f8aba23f 2060std::string GetTempDir() /*{{{*/
68e01721
MV
2061{
2062 const char *tmpdir = getenv("TMPDIR");
2063
2064#ifdef P_tmpdir
2065 if (!tmpdir)
2066 tmpdir = P_tmpdir;
2067#endif
2068
68e01721 2069 struct stat st;
0d303f17
DK
2070 if (!tmpdir || strlen(tmpdir) == 0 || // tmpdir is set
2071 stat(tmpdir, &st) != 0 || (st.st_mode & S_IFDIR) == 0 || // exists and is directory
2072 access(tmpdir, R_OK | W_OK | X_OK) != 0 // current user has rwx access to directory
2073 )
68e01721
MV
2074 tmpdir = "/tmp";
2075
2076 return string(tmpdir);
2077}
f8aba23f
DK
2078 /*}}}*/
2079FileFd* GetTempFile(std::string const &Prefix, bool ImmediateUnlink) /*{{{*/
0d29b9d4
MV
2080{
2081 char fn[512];
2082 FileFd *Fd = new FileFd();
2083
2084 std::string tempdir = GetTempDir();
2085 snprintf(fn, sizeof(fn), "%s/%s.XXXXXX",
2086 tempdir.c_str(), Prefix.c_str());
2087 int fd = mkstemp(fn);
2088 if(ImmediateUnlink)
2089 unlink(fn);
2090 if (fd < 0)
2091 {
2092 _error->Errno("GetTempFile",_("Unable to mkstemp %s"), fn);
2093 return NULL;
2094 }
2095 if (!Fd->OpenDescriptor(fd, FileFd::WriteOnly, FileFd::None, true))
2096 {
2097 _error->Errno("GetTempFile",_("Unable to write to %s"),fn);
2098 return NULL;
2099 }
2100
2101 return Fd;
2102}
f8aba23f
DK
2103 /*}}}*/
2104bool Rename(std::string From, std::string To) /*{{{*/
c1409d1b
MV
2105{
2106 if (rename(From.c_str(),To.c_str()) != 0)
2107 {
2108 _error->Error(_("rename failed, %s (%s -> %s)."),strerror(errno),
2109 From.c_str(),To.c_str());
2110 return false;
f8aba23f 2111 }
c1409d1b
MV
2112 return true;
2113}
f8aba23f
DK
2114 /*}}}*/
2115bool Popen(const char* Args[], FileFd &Fd, pid_t &Child, FileFd::OpenMode Mode)/*{{{*/
7ad2a347
MV
2116{
2117 int fd;
2118 if (Mode != FileFd::ReadOnly && Mode != FileFd::WriteOnly)
2119 return _error->Error("Popen supports ReadOnly (x)or WriteOnly mode only");
2120
2121 int Pipe[2] = {-1, -1};
2122 if(pipe(Pipe) != 0)
7ad2a347 2123 return _error->Errno("pipe", _("Failed to create subprocess IPC"));
5e49cbb7 2124
7ad2a347
MV
2125 std::set<int> keep_fds;
2126 keep_fds.insert(Pipe[0]);
2127 keep_fds.insert(Pipe[1]);
2128 Child = ExecFork(keep_fds);
2129 if(Child < 0)
2130 return _error->Errno("fork", "Failed to fork");
2131 if(Child == 0)
2132 {
2133 if(Mode == FileFd::ReadOnly)
2134 {
2135 close(Pipe[0]);
2136 fd = Pipe[1];
2137 }
2138 else if(Mode == FileFd::WriteOnly)
2139 {
2140 close(Pipe[1]);
2141 fd = Pipe[0];
2142 }
2143
2144 if(Mode == FileFd::ReadOnly)
2145 {
2146 dup2(fd, 1);
2147 dup2(fd, 2);
2148 } else if(Mode == FileFd::WriteOnly)
2149 dup2(fd, 0);
2150
2151 execv(Args[0], (char**)Args);
2152 _exit(100);
2153 }
2154 if(Mode == FileFd::ReadOnly)
2155 {
2156 close(Pipe[1]);
2157 fd = Pipe[0];
2158 } else if(Mode == FileFd::WriteOnly)
2159 {
2160 close(Pipe[0]);
2161 fd = Pipe[1];
2162 }
2163 Fd.OpenDescriptor(fd, Mode, FileFd::None, true);
2164
2165 return true;
2166}
f8aba23f
DK
2167 /*}}}*/
2168bool DropPrivileges() /*{{{*/
fc1a78d8 2169{
8f45798d
DK
2170 if(_config->FindB("Debug::NoDropPrivs", false) == true)
2171 return true;
2172
2173#if __gnu_linux__
2174#if defined(PR_SET_NO_NEW_PRIVS) && ( PR_SET_NO_NEW_PRIVS != 38 )
2175#error "PR_SET_NO_NEW_PRIVS is defined, but with a different value than expected!"
2176#endif
2177 // see prctl(2), needs linux3.5 at runtime - magic constant to avoid it at buildtime
2178 int ret = prctl(38, 1, 0, 0, 0);
2179 // ignore EINVAL - kernel is too old to understand the option
2180 if(ret < 0 && errno != EINVAL)
2181 _error->Warning("PR_SET_NO_NEW_PRIVS failed with %i", ret);
2182#endif
2183
990dd78a
DK
2184 // empty setting disables privilege dropping - this also ensures
2185 // backward compatibility, see bug #764506
2186 const std::string toUser = _config->Find("APT::Sandbox::User");
2187 if (toUser.empty())
2188 return true;
2189
f1e3c8f0 2190 // uid will be 0 in the end, but gid might be different anyway
8f45798d
DK
2191 uid_t const old_uid = getuid();
2192 gid_t const old_gid = getgid();
fc1a78d8 2193
5f2047ec
JAK
2194 if (old_uid != 0)
2195 return true;
3927c6da 2196
b8dae9a1 2197 struct passwd *pw = getpwnam(toUser.c_str());
fc1a78d8 2198 if (pw == NULL)
b8dae9a1 2199 return _error->Error("No user %s, can not drop rights", toUser.c_str());
3927c6da 2200
f1e3c8f0 2201 // Do not change the order here, it might break things
3b084f06 2202 if (setgroups(1, &pw->pw_gid))
3927c6da
MV
2203 return _error->Errno("setgroups", "Failed to setgroups");
2204
2205 if (setegid(pw->pw_gid) != 0)
5f2047ec
JAK
2206 return _error->Errno("setegid", "Failed to setegid");
2207
fc1a78d8
MV
2208 if (setgid(pw->pw_gid) != 0)
2209 return _error->Errno("setgid", "Failed to setgid");
5f2047ec 2210
fc1a78d8
MV
2211 if (setuid(pw->pw_uid) != 0)
2212 return _error->Errno("setuid", "Failed to setuid");
f1e3c8f0 2213
3927c6da
MV
2214 // the seteuid() is probably uneeded (at least thats what the linux
2215 // man-page says about setuid(2)) but we cargo culted it anyway
5f2047ec
JAK
2216 if (seteuid(pw->pw_uid) != 0)
2217 return _error->Errno("seteuid", "Failed to seteuid");
2218
f1e3c8f0 2219 // Verify that the user has only a single group, and the correct one
5f2047ec
JAK
2220 gid_t groups[1];
2221 if (getgroups(1, groups) != 1)
2222 return _error->Errno("getgroups", "Could not get new groups");
2223 if (groups[0] != pw->pw_gid)
2224 return _error->Error("Could not switch group");
f1e3c8f0
JAK
2225
2226 // Verify that gid, egid, uid, and euid changed
5f2047ec
JAK
2227 if (getgid() != pw->pw_gid)
2228 return _error->Error("Could not switch group");
2229 if (getegid() != pw->pw_gid)
2230 return _error->Error("Could not switch effective group");
2231 if (getuid() != pw->pw_uid)
2232 return _error->Error("Could not switch user");
2233 if (geteuid() != pw->pw_uid)
2234 return _error->Error("Could not switch effective user");
2235
550ab420 2236#ifdef HAVE_GETRESUID
48ed0977 2237 // verify that the saved set-user-id was changed as well
550ab420
JAK
2238 uid_t ruid = 0;
2239 uid_t euid = 0;
2240 uid_t suid = 0;
2241 if (getresuid(&ruid, &euid, &suid))
2242 return _error->Errno("getresuid", "Could not get saved set-user-ID");
2243 if (suid != pw->pw_uid)
2244 return _error->Error("Could not switch saved set-user-ID");
2245#endif
2246
2247#ifdef HAVE_GETRESGID
48ed0977 2248 // verify that the saved set-group-id was changed as well
550ab420
JAK
2249 gid_t rgid = 0;
2250 gid_t egid = 0;
2251 gid_t sgid = 0;
2252 if (getresgid(&rgid, &egid, &sgid))
2253 return _error->Errno("getresuid", "Could not get saved set-group-ID");
2254 if (sgid != pw->pw_gid)
2255 return _error->Error("Could not switch saved set-group-ID");
2256#endif
2257
bdc00df5
JAK
2258 // Check that uid and gid changes do not work anymore
2259 if (pw->pw_gid != old_gid && (setgid(old_gid) != -1 || setegid(old_gid) != -1))
2260 return _error->Error("Could restore a gid to root, privilege dropping did not work");
2261
2262 if (pw->pw_uid != old_uid && (setuid(old_uid) != -1 || seteuid(old_uid) != -1))
2263 return _error->Error("Could restore a uid to root, privilege dropping did not work");
2264
fc1a78d8
MV
2265 return true;
2266}
f8aba23f 2267 /*}}}*/