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