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