]> git.saurik.com Git - apt.git/blame - apt-pkg/contrib/fileutl.cc
force fancy progressbar redraw on window size change
[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 /*}}}*/
3b5421b4
AL
662// SetCloseExec - Set the close on exec flag /*{{{*/
663// ---------------------------------------------------------------------
664/* */
665void SetCloseExec(int Fd,bool Close)
666{
667 if (fcntl(Fd,F_SETFD,(Close == false)?0:FD_CLOEXEC) != 0)
668 {
669 cerr << "FATAL -> Could not set close on exec " << strerror(errno) << endl;
670 exit(100);
671 }
672}
673 /*}}}*/
674// SetNonBlock - Set the nonblocking flag /*{{{*/
675// ---------------------------------------------------------------------
676/* */
677void SetNonBlock(int Fd,bool Block)
678{
0a8a80e5
AL
679 int Flags = fcntl(Fd,F_GETFL) & (~O_NONBLOCK);
680 if (fcntl(Fd,F_SETFL,Flags | ((Block == false)?0:O_NONBLOCK)) != 0)
3b5421b4
AL
681 {
682 cerr << "FATAL -> Could not set non-blocking flag " << strerror(errno) << endl;
683 exit(100);
684 }
685}
686 /*}}}*/
687// WaitFd - Wait for a FD to become readable /*{{{*/
688// ---------------------------------------------------------------------
b2e465d6 689/* This waits for a FD to become readable using select. It is useful for
6d5dd02a
AL
690 applications making use of non-blocking sockets. The timeout is
691 in seconds. */
1084d58a 692bool WaitFd(int Fd,bool write,unsigned long timeout)
3b5421b4
AL
693{
694 fd_set Set;
cc2313b7 695 struct timeval tv;
3b5421b4
AL
696 FD_ZERO(&Set);
697 FD_SET(Fd,&Set);
6d5dd02a
AL
698 tv.tv_sec = timeout;
699 tv.tv_usec = 0;
1084d58a 700 if (write == true)
b0db36b1
AL
701 {
702 int Res;
703 do
704 {
705 Res = select(Fd+1,0,&Set,0,(timeout != 0?&tv:0));
706 }
707 while (Res < 0 && errno == EINTR);
708
709 if (Res <= 0)
710 return false;
1084d58a
AL
711 }
712 else
713 {
b0db36b1
AL
714 int Res;
715 do
716 {
717 Res = select(Fd+1,&Set,0,0,(timeout != 0?&tv:0));
718 }
719 while (Res < 0 && errno == EINTR);
720
721 if (Res <= 0)
722 return false;
cc2313b7 723 }
1084d58a 724
3b5421b4
AL
725 return true;
726}
727 /*}}}*/
96ae6de5 728// MergeKeepFdsFromConfiguration - Merge APT::Keep-Fds configuration /*{{{*/
54676e1a 729// ---------------------------------------------------------------------
96ae6de5
MV
730/* This is used to merge the APT::Keep-Fds with the provided KeepFDs
731 * set.
732 */
733void MergeKeepFdsFromConfiguration(std::set<int> &KeepFDs)
e45c4617 734{
e45c4617
MV
735 Configuration::Item const *Opts = _config->Tree("APT::Keep-Fds");
736 if (Opts != 0 && Opts->Child != 0)
737 {
738 Opts = Opts->Child;
739 for (; Opts != 0; Opts = Opts->Next)
740 {
741 if (Opts->Value.empty() == true)
742 continue;
743 int fd = atoi(Opts->Value.c_str());
744 KeepFDs.insert(fd);
745 }
746 }
96ae6de5
MV
747}
748 /*}}}*/
749// ExecFork - Magical fork that sanitizes the context before execing /*{{{*/
750// ---------------------------------------------------------------------
751/* This is used if you want to cleanse the environment for the forked
752 child, it fixes up the important signals and nukes all of the fds,
753 otherwise acts like normal fork. */
754pid_t ExecFork()
755{
756 set<int> KeepFDs;
757 // we need to merge the Keep-Fds as external tools like
758 // debconf-apt-progress use it
759 MergeKeepFdsFromConfiguration(KeepFDs);
e45c4617
MV
760 return ExecFork(KeepFDs);
761}
762
763pid_t ExecFork(std::set<int> KeepFDs)
54676e1a
AL
764{
765 // Fork off the process
766 pid_t Process = fork();
767 if (Process < 0)
768 {
769 cerr << "FATAL -> Failed to fork." << endl;
770 exit(100);
771 }
772
773 // Spawn the subprocess
774 if (Process == 0)
775 {
776 // Setup the signals
777 signal(SIGPIPE,SIG_DFL);
778 signal(SIGQUIT,SIG_DFL);
779 signal(SIGINT,SIG_DFL);
780 signal(SIGWINCH,SIG_DFL);
781 signal(SIGCONT,SIG_DFL);
782 signal(SIGTSTP,SIG_DFL);
75ef8f14 783
54676e1a 784 // Close all of our FDs - just in case
61f954bf 785 for (int K = 3; K != sysconf(_SC_OPEN_MAX); K++)
75ef8f14
MV
786 {
787 if(KeepFDs.find(K) == KeepFDs.end())
007dc9e0 788 fcntl(K,F_SETFD,FD_CLOEXEC);
75ef8f14 789 }
54676e1a
AL
790 }
791
792 return Process;
793}
794 /*}}}*/
ddc1d8d0
AL
795// ExecWait - Fancy waitpid /*{{{*/
796// ---------------------------------------------------------------------
2c9a72d1 797/* Waits for the given sub process. If Reap is set then no errors are
ddc1d8d0
AL
798 generated. Otherwise a failed subprocess will generate a proper descriptive
799 message */
3826564e 800bool ExecWait(pid_t Pid,const char *Name,bool Reap)
ddc1d8d0
AL
801{
802 if (Pid <= 1)
803 return true;
804
805 // Wait and collect the error code
806 int Status;
807 while (waitpid(Pid,&Status,0) != Pid)
808 {
809 if (errno == EINTR)
810 continue;
811
812 if (Reap == true)
813 return false;
814
db0db9fe 815 return _error->Error(_("Waited for %s but it wasn't there"),Name);
ddc1d8d0
AL
816 }
817
818
819 // Check for an error code.
820 if (WIFEXITED(Status) == 0 || WEXITSTATUS(Status) != 0)
821 {
822 if (Reap == true)
823 return false;
ab7f4d7c 824 if (WIFSIGNALED(Status) != 0)
40e7fe0e 825 {
ab7f4d7c
MV
826 if( WTERMSIG(Status) == SIGSEGV)
827 return _error->Error(_("Sub-process %s received a segmentation fault."),Name);
828 else
829 return _error->Error(_("Sub-process %s received signal %u."),Name, WTERMSIG(Status));
40e7fe0e 830 }
ddc1d8d0
AL
831
832 if (WIFEXITED(Status) != 0)
b2e465d6 833 return _error->Error(_("Sub-process %s returned an error code (%u)"),Name,WEXITSTATUS(Status));
ddc1d8d0 834
b2e465d6 835 return _error->Error(_("Sub-process %s exited unexpectedly"),Name);
ddc1d8d0
AL
836 }
837
838 return true;
839}
840 /*}}}*/
578bfd0a 841
4239dbca
DK
842class FileFdPrivate { /*{{{*/
843 public:
844#ifdef HAVE_ZLIB
845 gzFile gz;
846#endif
847#ifdef HAVE_BZ2
848 BZFILE* bz2;
849#endif
850#ifdef HAVE_LZMA
851 struct LZMAFILE {
852 FILE* file;
853 uint8_t buffer[4096];
854 lzma_stream stream;
855 lzma_ret err;
856 bool eof;
857 bool compressing;
858
859 LZMAFILE() : file(NULL), eof(false), compressing(false) {}
860 ~LZMAFILE() {
861 if (compressing == true)
862 {
863 for (;;) {
864 stream.avail_out = sizeof(buffer)/sizeof(buffer[0]);
865 stream.next_out = buffer;
866 err = lzma_code(&stream, LZMA_FINISH);
867 if (err != LZMA_OK && err != LZMA_STREAM_END)
868 {
869 _error->Error("~LZMAFILE: Compress finalisation failed");
870 break;
871 }
872 size_t const n = sizeof(buffer)/sizeof(buffer[0]) - stream.avail_out;
873 if (n && fwrite(buffer, 1, n, file) != n)
874 {
875 _error->Errno("~LZMAFILE",_("Write error"));
876 break;
877 }
878 if (err == LZMA_STREAM_END)
879 break;
880 }
881 }
882 lzma_end(&stream);
883 fclose(file);
884 }
885 };
886 LZMAFILE* lzma;
887#endif
888 int compressed_fd;
889 pid_t compressor_pid;
890 bool pipe;
891 APT::Configuration::Compressor compressor;
892 unsigned int openmode;
893 unsigned long long seekpos;
894 FileFdPrivate() :
895#ifdef HAVE_ZLIB
896 gz(NULL),
897#endif
898#ifdef HAVE_BZ2
899 bz2(NULL),
900#endif
901#ifdef HAVE_LZMA
902 lzma(NULL),
903#endif
904 compressed_fd(-1), compressor_pid(-1), pipe(false),
905 openmode(0), seekpos(0) {};
906 bool InternalClose(std::string const &FileName)
907 {
908 if (false)
909 /* dummy so that the rest can be 'else if's */;
910#ifdef HAVE_ZLIB
911 else if (gz != NULL) {
912 int const e = gzclose(gz);
913 gz = NULL;
914 // gzdclose() on empty files always fails with "buffer error" here, ignore that
915 if (e != 0 && e != Z_BUF_ERROR)
916 return _error->Errno("close",_("Problem closing the gzip file %s"), FileName.c_str());
917 }
918#endif
919#ifdef HAVE_BZ2
920 else if (bz2 != NULL) {
921 BZ2_bzclose(bz2);
922 bz2 = NULL;
923 }
924#endif
925#ifdef HAVE_LZMA
926 else if (lzma != NULL) {
927 delete lzma;
928 lzma = NULL;
929 }
930#endif
931 return true;
932 }
933 bool CloseDown(std::string const &FileName)
934 {
935 bool const Res = InternalClose(FileName);
936
937 if (compressor_pid > 0)
938 ExecWait(compressor_pid, "FileFdCompressor", true);
939 compressor_pid = -1;
940
941 return Res;
942 }
943 bool InternalStream() const {
944 return false
945#ifdef HAVE_BZ2
946 || bz2 != NULL
947#endif
948#ifdef HAVE_LZMA
949 || lzma != NULL
950#endif
951 ;
952 }
953
954
955 ~FileFdPrivate() { CloseDown(""); }
956};
957 /*}}}*/
13d87e2e 958// FileFd::Open - Open a file /*{{{*/
578bfd0a
AL
959// ---------------------------------------------------------------------
960/* The most commonly used open mode combinations are given with Mode */
e5f3f8c1 961bool FileFd::Open(string FileName,unsigned int const Mode,CompressMode Compress, unsigned long const AccessMode)
578bfd0a 962{
257e8d66 963 if (Mode == ReadOnlyGzip)
e5f3f8c1 964 return Open(FileName, ReadOnly, Gzip, AccessMode);
257e8d66 965
468720c5 966 if (Compress == Auto && (Mode & WriteOnly) == WriteOnly)
ae635e3c 967 return FileFdError("Autodetection on %s only works in ReadOnly openmode!", FileName.c_str());
257e8d66 968
468720c5
DK
969 std::vector<APT::Configuration::Compressor> const compressors = APT::Configuration::getCompressors();
970 std::vector<APT::Configuration::Compressor>::const_iterator compressor = compressors.begin();
971 if (Compress == Auto)
972 {
468720c5
DK
973 for (; compressor != compressors.end(); ++compressor)
974 {
e788a834 975 std::string file = FileName + compressor->Extension;
468720c5
DK
976 if (FileExists(file) == false)
977 continue;
978 FileName = file;
468720c5
DK
979 break;
980 }
981 }
982 else if (Compress == Extension)
983 {
52b47296
DK
984 std::string::size_type const found = FileName.find_last_of('.');
985 std::string ext;
986 if (found != std::string::npos)
987 {
988 ext = FileName.substr(found);
989 if (ext == ".new" || ext == ".bak")
990 {
991 std::string::size_type const found2 = FileName.find_last_of('.', found - 1);
992 if (found2 != std::string::npos)
993 ext = FileName.substr(found2, found - found2);
994 else
995 ext.clear();
996 }
997 }
aee1aac6
DK
998 for (; compressor != compressors.end(); ++compressor)
999 if (ext == compressor->Extension)
1000 break;
1001 // no matching extension - assume uncompressed (imagine files like 'example.org_Packages')
1002 if (compressor == compressors.end())
1003 for (compressor = compressors.begin(); compressor != compressors.end(); ++compressor)
1004 if (compressor->Name == ".")
468720c5 1005 break;
468720c5 1006 }
aee1aac6 1007 else
468720c5
DK
1008 {
1009 std::string name;
1010 switch (Compress)
1011 {
aee1aac6 1012 case None: name = "."; break;
468720c5
DK
1013 case Gzip: name = "gzip"; break;
1014 case Bzip2: name = "bzip2"; break;
1015 case Lzma: name = "lzma"; break;
1016 case Xz: name = "xz"; break;
aee1aac6
DK
1017 case Auto:
1018 case Extension:
52b47296 1019 // Unreachable
ae635e3c 1020 return FileFdError("Opening File %s in None, Auto or Extension should be already handled?!?", FileName.c_str());
468720c5
DK
1021 }
1022 for (; compressor != compressors.end(); ++compressor)
1023 if (compressor->Name == name)
1024 break;
aee1aac6 1025 if (compressor == compressors.end())
ae635e3c 1026 return FileFdError("Can't find a configured compressor %s for file %s", name.c_str(), FileName.c_str());
468720c5
DK
1027 }
1028
aee1aac6 1029 if (compressor == compressors.end())
ae635e3c 1030 return FileFdError("Can't find a match for specified compressor mode for file %s", FileName.c_str());
e5f3f8c1 1031 return Open(FileName, Mode, *compressor, AccessMode);
aee1aac6 1032}
e5f3f8c1 1033bool FileFd::Open(string FileName,unsigned int const Mode,APT::Configuration::Compressor const &compressor, unsigned long const AccessMode)
aee1aac6
DK
1034{
1035 Close();
aee1aac6
DK
1036 Flags = AutoClose;
1037
1038 if ((Mode & WriteOnly) != WriteOnly && (Mode & (Atomic | Create | Empty | Exclusive)) != 0)
ae635e3c 1039 return FileFdError("ReadOnly mode for %s doesn't accept additional flags!", FileName.c_str());
aee1aac6 1040 if ((Mode & ReadWrite) == 0)
ae635e3c 1041 return FileFdError("No openmode provided in FileFd::Open for %s", FileName.c_str());
468720c5 1042
257e8d66
DK
1043 if ((Mode & Atomic) == Atomic)
1044 {
1045 Flags |= Replace;
257e8d66
DK
1046 }
1047 else if ((Mode & (Exclusive | Create)) == (Exclusive | Create))
1048 {
1049 // for atomic, this will be done by rename in Close()
1050 unlink(FileName.c_str());
1051 }
1052 if ((Mode & Empty) == Empty)
578bfd0a 1053 {
257e8d66
DK
1054 struct stat Buf;
1055 if (lstat(FileName.c_str(),&Buf) == 0 && S_ISLNK(Buf.st_mode))
1056 unlink(FileName.c_str());
1057 }
c4fc2fd7 1058
561f860a
DK
1059 int fileflags = 0;
1060 #define if_FLAGGED_SET(FLAG, MODE) if ((Mode & FLAG) == FLAG) fileflags |= MODE
1061 if_FLAGGED_SET(ReadWrite, O_RDWR);
1062 else if_FLAGGED_SET(ReadOnly, O_RDONLY);
1063 else if_FLAGGED_SET(WriteOnly, O_WRONLY);
4a9db827 1064
561f860a
DK
1065 if_FLAGGED_SET(Create, O_CREAT);
1066 if_FLAGGED_SET(Empty, O_TRUNC);
1067 if_FLAGGED_SET(Exclusive, O_EXCL);
561f860a 1068 #undef if_FLAGGED_SET
52b47296 1069
7335eebe
AGM
1070 if ((Mode & Atomic) == Atomic)
1071 {
1072 char *name = strdup((FileName + ".XXXXXX").c_str());
1073
dc545c0b 1074 if((iFd = mkstemp(name)) == -1)
7335eebe
AGM
1075 {
1076 free(name);
98b69f9d 1077 return FileFdErrno("mkstemp", "Could not create temporary file for %s", FileName.c_str());
7335eebe
AGM
1078 }
1079
1080 TemporaryFileName = string(name);
7335eebe 1081 free(name);
dc545c0b 1082
230e69d7
DK
1083 // umask() will always set the umask and return the previous value, so
1084 // we first set the umask and then reset it to the old value
1085 mode_t const CurrentUmask = umask(0);
1086 umask(CurrentUmask);
1087 // calculate the actual file permissions (just like open/creat)
1088 mode_t const FilePermissions = (AccessMode & ~CurrentUmask);
1089
1090 if(fchmod(iFd, FilePermissions) == -1)
dc545c0b 1091 return FileFdErrno("fchmod", "Could not change permissions for temporary file %s", TemporaryFileName.c_str());
7335eebe 1092 }
468720c5 1093 else
230e69d7 1094 iFd = open(FileName.c_str(), fileflags, AccessMode);
468720c5 1095
b711c01e 1096 this->FileName = FileName;
561f860a
DK
1097 if (iFd == -1 || OpenInternDescriptor(Mode, compressor) == false)
1098 {
468720c5 1099 if (iFd != -1)
fc81e8f2 1100 {
561f860a
DK
1101 close (iFd);
1102 iFd = -1;
fc81e8f2 1103 }
ae635e3c 1104 return FileFdErrno("open",_("Could not open file %s"), FileName.c_str());
257e8d66 1105 }
578bfd0a 1106
13d87e2e
AL
1107 SetCloseExec(iFd,true);
1108 return true;
578bfd0a 1109}
257e8d66
DK
1110 /*}}}*/
1111// FileFd::OpenDescriptor - Open a filedescriptor /*{{{*/
1112// ---------------------------------------------------------------------
1113/* */
52b47296 1114bool FileFd::OpenDescriptor(int Fd, unsigned int const Mode, CompressMode Compress, bool AutoClose)
aee1aac6
DK
1115{
1116 std::vector<APT::Configuration::Compressor> const compressors = APT::Configuration::getCompressors();
1117 std::vector<APT::Configuration::Compressor>::const_iterator compressor = compressors.begin();
1118 std::string name;
bce778a3
MV
1119
1120 // compat with the old API
1121 if (Mode == ReadOnlyGzip && Compress == None)
1122 Compress = Gzip;
1123
aee1aac6
DK
1124 switch (Compress)
1125 {
1126 case None: name = "."; break;
1127 case Gzip: name = "gzip"; break;
1128 case Bzip2: name = "bzip2"; break;
1129 case Lzma: name = "lzma"; break;
1130 case Xz: name = "xz"; break;
1131 case Auto:
1132 case Extension:
f97bb523
DK
1133 if (AutoClose == true && Fd != -1)
1134 close(Fd);
ae635e3c 1135 return FileFdError("Opening Fd %d in Auto or Extension compression mode is not supported", Fd);
aee1aac6
DK
1136 }
1137 for (; compressor != compressors.end(); ++compressor)
1138 if (compressor->Name == name)
1139 break;
1140 if (compressor == compressors.end())
f97bb523
DK
1141 {
1142 if (AutoClose == true && Fd != -1)
1143 close(Fd);
ae635e3c 1144 return FileFdError("Can't find a configured compressor %s for file %s", name.c_str(), FileName.c_str());
f97bb523 1145 }
aee1aac6
DK
1146 return OpenDescriptor(Fd, Mode, *compressor, AutoClose);
1147}
52b47296 1148bool FileFd::OpenDescriptor(int Fd, unsigned int const Mode, APT::Configuration::Compressor const &compressor, bool AutoClose)
144c0969
JAK
1149{
1150 Close();
1151 Flags = (AutoClose) ? FileFd::AutoClose : 0;
84baaae9 1152 iFd = Fd;
b711c01e 1153 this->FileName = "";
84baaae9 1154 if (OpenInternDescriptor(Mode, compressor) == false)
468720c5 1155 {
f97bb523 1156 if (iFd != -1 && (
84baaae9 1157 (Flags & Compressed) == Compressed ||
f97bb523
DK
1158 AutoClose == true))
1159 {
468720c5 1160 close (iFd);
f97bb523
DK
1161 iFd = -1;
1162 }
1163 return FileFdError(_("Could not open file descriptor %d"), Fd);
144c0969 1164 }
144c0969 1165 return true;
468720c5 1166}
52b47296 1167bool FileFd::OpenInternDescriptor(unsigned int const Mode, APT::Configuration::Compressor const &compressor)
468720c5 1168{
84baaae9
DK
1169 if (iFd == -1)
1170 return false;
ff477ee1
DK
1171 if (compressor.Name == "." || compressor.Binary.empty() == true)
1172 return true;
1173
7f350a37 1174#if defined HAVE_ZLIB || defined HAVE_BZ2 || defined HAVE_LZMA
69d6988a
DK
1175 // the API to open files is similar, so setup to avoid code duplicates later
1176 // and while at it ensure that we close before opening (if its a reopen)
1177 void* (*compress_open)(int, const char *) = NULL;
7f350a37
DK
1178 if (false)
1179 /* dummy so that the rest can be 'else if's */;
4239dbca 1180#define APT_COMPRESS_INIT(NAME,OPEN) \
7f350a37 1181 else if (compressor.Name == NAME) \
69d6988a
DK
1182 { \
1183 compress_open = (void*(*)(int, const char *)) OPEN; \
4239dbca 1184 if (d != NULL) d->InternalClose(FileName); \
69d6988a
DK
1185 }
1186#ifdef HAVE_ZLIB
4239dbca 1187 APT_COMPRESS_INIT("gzip", gzdopen)
69d6988a
DK
1188#endif
1189#ifdef HAVE_BZ2
4239dbca 1190 APT_COMPRESS_INIT("bzip2", BZ2_bzdopen)
69d6988a 1191#endif
7f350a37 1192#ifdef HAVE_LZMA
4239dbca
DK
1193 APT_COMPRESS_INIT("xz", fdopen)
1194 APT_COMPRESS_INIT("lzma", fdopen)
7f350a37 1195#endif
69d6988a
DK
1196#undef APT_COMPRESS_INIT
1197#endif
1198
ba667cf7
DK
1199 if (d == NULL)
1200 {
1201 d = new FileFdPrivate();
1202 d->openmode = Mode;
1203 d->compressor = compressor;
7f350a37 1204#if defined HAVE_ZLIB || defined HAVE_BZ2 || defined HAVE_LZMA
f6ffe501 1205 if ((Flags & AutoClose) != AutoClose && compress_open != NULL)
84baaae9
DK
1206 {
1207 // Need to duplicate fd here or gz/bz2 close for cleanup will close the fd as well
1208 int const internFd = dup(iFd);
1209 if (internFd == -1)
1210 return FileFdErrno("OpenInternDescriptor", _("Could not open file descriptor %d"), iFd);
1211 iFd = internFd;
1212 }
69d6988a 1213#endif
ba667cf7 1214 }
ff477ee1 1215
7f350a37 1216#if defined HAVE_ZLIB || defined HAVE_BZ2 || defined HAVE_LZMA
69d6988a 1217 if (compress_open != NULL)
468720c5 1218 {
69d6988a 1219 void* compress_struct = NULL;
468720c5 1220 if ((Mode & ReadWrite) == ReadWrite)
69d6988a 1221 compress_struct = compress_open(iFd, "r+");
468720c5 1222 else if ((Mode & WriteOnly) == WriteOnly)
69d6988a 1223 compress_struct = compress_open(iFd, "w");
468720c5 1224 else
69d6988a
DK
1225 compress_struct = compress_open(iFd, "r");
1226 if (compress_struct == NULL)
468720c5 1227 return false;
69d6988a 1228
7f350a37
DK
1229 if (false)
1230 /* dummy so that the rest can be 'else if's */;
69d6988a 1231#ifdef HAVE_ZLIB
7f350a37 1232 else if (compressor.Name == "gzip")
69d6988a 1233 d->gz = (gzFile) compress_struct;
699b209e 1234#endif
c4997486 1235#ifdef HAVE_BZ2
7f350a37 1236 else if (compressor.Name == "bzip2")
69d6988a 1237 d->bz2 = (BZFILE*) compress_struct;
7f350a37
DK
1238#endif
1239#ifdef HAVE_LZMA
1240 else if (compressor.Name == "xz" || compressor.Name == "lzma")
adbd7eb4 1241 {
7f350a37
DK
1242 uint32_t const xzlevel = 6;
1243 uint64_t const memlimit = UINT64_MAX;
1244 if (d->lzma == NULL)
1245 d->lzma = new FileFdPrivate::LZMAFILE;
1246 d->lzma->file = (FILE*) compress_struct;
1247 d->lzma->stream = LZMA_STREAM_INIT;
1248
1249 if ((Mode & ReadWrite) == ReadWrite)
1250 return FileFdError("ReadWrite mode is not supported for file %s", FileName.c_str());
1251
1252 if ((Mode & WriteOnly) == WriteOnly)
1253 {
1254 if (compressor.Name == "xz")
1255 {
1256 if (lzma_easy_encoder(&d->lzma->stream, xzlevel, LZMA_CHECK_CRC32) != LZMA_OK)
1257 return false;
1258 }
1259 else
1260 {
1261 lzma_options_lzma options;
1262 lzma_lzma_preset(&options, xzlevel);
1263 if (lzma_alone_encoder(&d->lzma->stream, &options) != LZMA_OK)
1264 return false;
1265 }
1266 d->lzma->compressing = true;
1267 }
1268 else
1269 {
1270 if (compressor.Name == "xz")
1271 {
1272 if (lzma_auto_decoder(&d->lzma->stream, memlimit, 0) != LZMA_OK)
1273 return false;
1274 }
1275 else
1276 {
1277 if (lzma_alone_decoder(&d->lzma->stream, memlimit) != LZMA_OK)
1278 return false;
1279 }
1280 d->lzma->compressing = false;
1281 }
adbd7eb4 1282 }
69d6988a 1283#endif
c4997486
DK
1284 Flags |= Compressed;
1285 return true;
1286 }
1287#endif
1288
adbd7eb4
DK
1289 // collect zombies here in case we reopen
1290 if (d->compressor_pid > 0)
1291 ExecWait(d->compressor_pid, "FileFdCompressor", true);
561f860a
DK
1292
1293 if ((Mode & ReadWrite) == ReadWrite)
ae635e3c 1294 return FileFdError("ReadWrite mode is not supported for file %s", FileName.c_str());
561f860a
DK
1295
1296 bool const Comp = (Mode & WriteOnly) == WriteOnly;
561f860a
DK
1297 if (Comp == false)
1298 {
adbd7eb4 1299 // Handle 'decompression' of empty files
561f860a
DK
1300 struct stat Buf;
1301 fstat(iFd, &Buf);
1302 if (Buf.st_size == 0 && S_ISFIFO(Buf.st_mode) == false)
1303 return true;
1304
1305 // We don't need the file open - instead let the compressor open it
1306 // as he properly knows better how to efficiently read from 'his' file
1307 if (FileName.empty() == false)
afb093cd 1308 {
561f860a 1309 close(iFd);
afb093cd
DK
1310 iFd = -1;
1311 }
561f860a
DK
1312 }
1313
1314 // Create a data pipe
1315 int Pipe[2] = {-1,-1};
1316 if (pipe(Pipe) != 0)
ae635e3c 1317 return FileFdErrno("pipe",_("Failed to create subprocess IPC"));
561f860a
DK
1318 for (int J = 0; J != 2; J++)
1319 SetCloseExec(Pipe[J],true);
1320
1321 d->compressed_fd = iFd;
1322 d->pipe = true;
1323
1324 if (Comp == true)
1325 iFd = Pipe[1];
1326 else
1327 iFd = Pipe[0];
1328
1329 // The child..
1330 d->compressor_pid = ExecFork();
1331 if (d->compressor_pid == 0)
1332 {
1333 if (Comp == true)
1334 {
1335 dup2(d->compressed_fd,STDOUT_FILENO);
1336 dup2(Pipe[0],STDIN_FILENO);
1337 }
1338 else
1339 {
7f350a37 1340 if (d->compressed_fd != -1)
561f860a
DK
1341 dup2(d->compressed_fd,STDIN_FILENO);
1342 dup2(Pipe[1],STDOUT_FILENO);
1343 }
0b4895d3
DK
1344 int const nullfd = open("/dev/null", O_WRONLY);
1345 if (nullfd != -1)
1346 {
1347 dup2(nullfd,STDERR_FILENO);
1348 close(nullfd);
1349 }
561f860a
DK
1350
1351 SetCloseExec(STDOUT_FILENO,false);
1352 SetCloseExec(STDIN_FILENO,false);
1353
1354 std::vector<char const*> Args;
1355 Args.push_back(compressor.Binary.c_str());
1356 std::vector<std::string> const * const addArgs =
1357 (Comp == true) ? &(compressor.CompressArgs) : &(compressor.UncompressArgs);
1358 for (std::vector<std::string>::const_iterator a = addArgs->begin();
1359 a != addArgs->end(); ++a)
1360 Args.push_back(a->c_str());
1361 if (Comp == false && FileName.empty() == false)
1362 {
1363 Args.push_back("--stdout");
1364 if (TemporaryFileName.empty() == false)
1365 Args.push_back(TemporaryFileName.c_str());
1366 else
1367 Args.push_back(FileName.c_str());
1368 }
1369 Args.push_back(NULL);
1370
1371 execvp(Args[0],(char **)&Args[0]);
1372 cerr << _("Failed to exec compressor ") << Args[0] << endl;
1373 _exit(100);
1374 }
1375 if (Comp == true)
1376 close(Pipe[0]);
468720c5 1377 else
561f860a 1378 close(Pipe[1]);
561f860a 1379
468720c5 1380 return true;
144c0969 1381}
578bfd0a 1382 /*}}}*/
8e06abb2 1383// FileFd::~File - Closes the file /*{{{*/
578bfd0a
AL
1384// ---------------------------------------------------------------------
1385/* If the proper modes are selected then we close the Fd and possibly
1386 unlink the file on error. */
8e06abb2 1387FileFd::~FileFd()
578bfd0a
AL
1388{
1389 Close();
500400fe 1390 if (d != NULL)
500400fe 1391 d->CloseDown(FileName);
96ab3c6f
MV
1392 delete d;
1393 d = NULL;
578bfd0a
AL
1394}
1395 /*}}}*/
8e06abb2 1396// FileFd::Read - Read a bit of the file /*{{{*/
578bfd0a 1397// ---------------------------------------------------------------------
1e3f4083 1398/* We are careful to handle interruption by a signal while reading
b0db36b1 1399 gracefully. */
650faab0 1400bool FileFd::Read(void *To,unsigned long long Size,unsigned long long *Actual)
578bfd0a 1401{
3286ad13 1402 ssize_t Res;
b0db36b1 1403 errno = 0;
f604cf55
AL
1404 if (Actual != 0)
1405 *Actual = 0;
699b209e 1406 *((char *)To) = '\0';
b0db36b1 1407 do
578bfd0a 1408 {
7f350a37
DK
1409 if (false)
1410 /* dummy so that the rest can be 'else if's */;
7efb8c8e 1411#ifdef HAVE_ZLIB
7f350a37 1412 else if (d != NULL && d->gz != NULL)
c4997486 1413 Res = gzread(d->gz,To,Size);
c4997486
DK
1414#endif
1415#ifdef HAVE_BZ2
7f350a37 1416 else if (d != NULL && d->bz2 != NULL)
c4997486 1417 Res = BZ2_bzread(d->bz2,To,Size);
699b209e 1418#endif
7f350a37
DK
1419#ifdef HAVE_LZMA
1420 else if (d != NULL && d->lzma != NULL)
1421 {
1422 if (d->lzma->eof == true)
1423 break;
1424
1425 d->lzma->stream.next_out = (uint8_t *) To;
1426 d->lzma->stream.avail_out = Size;
1427 if (d->lzma->stream.avail_in == 0)
1428 {
1429 d->lzma->stream.next_in = d->lzma->buffer;
1430 d->lzma->stream.avail_in = fread(d->lzma->buffer, 1, sizeof(d->lzma->buffer)/sizeof(d->lzma->buffer[0]), d->lzma->file);
1431 }
1432 d->lzma->err = lzma_code(&d->lzma->stream, LZMA_RUN);
1433 if (d->lzma->err == LZMA_STREAM_END)
1434 {
1435 d->lzma->eof = true;
1436 Res = Size - d->lzma->stream.avail_out;
1437 }
1438 else if (d->lzma->err != LZMA_OK)
1439 {
1440 Res = -1;
1441 errno = 0;
1442 }
1443 else
c4b113e6 1444 {
7f350a37 1445 Res = Size - d->lzma->stream.avail_out;
c4b113e6
DK
1446 if (Res == 0)
1447 {
1448 // lzma run was okay, but produced no output…
1449 Res = -1;
1450 errno = EINTR;
1451 }
1452 }
7f350a37
DK
1453 }
1454#endif
1455 else
a3a03f5d 1456 Res = read(iFd,To,Size);
b711c01e 1457
b0db36b1
AL
1458 if (Res < 0)
1459 {
b711c01e 1460 if (errno == EINTR)
c4b113e6
DK
1461 {
1462 // trick the while-loop into running again
1463 Res = 1;
1464 errno = 0;
b711c01e 1465 continue;
c4b113e6 1466 }
7f350a37
DK
1467 if (false)
1468 /* dummy so that the rest can be 'else if's */;
7efb8c8e 1469#ifdef HAVE_ZLIB
7f350a37 1470 else if (d != NULL && d->gz != NULL)
b711c01e
DK
1471 {
1472 int err;
1473 char const * const errmsg = gzerror(d->gz, &err);
1474 if (err != Z_ERRNO)
ae635e3c 1475 return FileFdError("gzread: %s (%d: %s)", _("Read error"), err, errmsg);
b711c01e 1476 }
c4997486
DK
1477#endif
1478#ifdef HAVE_BZ2
7f350a37 1479 else if (d != NULL && d->bz2 != NULL)
c4997486
DK
1480 {
1481 int err;
1482 char const * const errmsg = BZ2_bzerror(d->bz2, &err);
1483 if (err != BZ_IO_ERROR)
ae635e3c 1484 return FileFdError("BZ2_bzread: %s (%d: %s)", _("Read error"), err, errmsg);
c4997486 1485 }
7f350a37
DK
1486#endif
1487#ifdef HAVE_LZMA
1488 else if (d != NULL && d->lzma != NULL)
1489 return FileFdError("lzma_read: %s (%d)", _("Read error"), d->lzma->err);
b711c01e 1490#endif
ae635e3c 1491 return FileFdErrno("read",_("Read error"));
b0db36b1 1492 }
578bfd0a 1493
b0db36b1
AL
1494 To = (char *)To + Res;
1495 Size -= Res;
ff477ee1
DK
1496 if (d != NULL)
1497 d->seekpos += Res;
f604cf55
AL
1498 if (Actual != 0)
1499 *Actual += Res;
b0db36b1
AL
1500 }
1501 while (Res > 0 && Size > 0);
1502
1503 if (Size == 0)
1504 return true;
1505
ddc1d8d0 1506 // Eof handling
f604cf55 1507 if (Actual != 0)
ddc1d8d0
AL
1508 {
1509 Flags |= HitEof;
1510 return true;
1511 }
ae635e3c
DK
1512
1513 return FileFdError(_("read, still have %llu to read but none left"), Size);
578bfd0a
AL
1514}
1515 /*}}}*/
032bd56f
DK
1516// FileFd::ReadLine - Read a complete line from the file /*{{{*/
1517// ---------------------------------------------------------------------
1518/* Beware: This method can be quiet slow for big buffers on UNcompressed
1519 files because of the naive implementation! */
1520char* FileFd::ReadLine(char *To, unsigned long long const Size)
1521{
699b209e 1522 *To = '\0';
7efb8c8e 1523#ifdef HAVE_ZLIB
ff477ee1 1524 if (d != NULL && d->gz != NULL)
032bd56f 1525 return gzgets(d->gz, To, Size);
699b209e 1526#endif
032bd56f
DK
1527
1528 unsigned long long read = 0;
40468850
DK
1529 while ((Size - 1) != read)
1530 {
1531 unsigned long long done = 0;
1532 if (Read(To + read, 1, &done) == false)
1533 return NULL;
1534 if (done == 0)
1535 break;
1536 if (To[read++] == '\n')
1537 break;
1538 }
1539 if (read == 0)
032bd56f 1540 return NULL;
40468850 1541 To[read] = '\0';
032bd56f
DK
1542 return To;
1543}
1544 /*}}}*/
8e06abb2 1545// FileFd::Write - Write to the file /*{{{*/
578bfd0a
AL
1546// ---------------------------------------------------------------------
1547/* */
650faab0 1548bool FileFd::Write(const void *From,unsigned long long Size)
578bfd0a 1549{
3286ad13 1550 ssize_t Res;
b0db36b1
AL
1551 errno = 0;
1552 do
578bfd0a 1553 {
7f350a37
DK
1554 if (false)
1555 /* dummy so that the rest can be 'else if's */;
7efb8c8e 1556#ifdef HAVE_ZLIB
7f350a37
DK
1557 else if (d != NULL && d->gz != NULL)
1558 Res = gzwrite(d->gz,From,Size);
c4997486
DK
1559#endif
1560#ifdef HAVE_BZ2
7f350a37
DK
1561 else if (d != NULL && d->bz2 != NULL)
1562 Res = BZ2_bzwrite(d->bz2,(void*)From,Size);
699b209e 1563#endif
7f350a37
DK
1564#ifdef HAVE_LZMA
1565 else if (d != NULL && d->lzma != NULL)
1566 {
1567 d->lzma->stream.next_in = (uint8_t *)From;
1568 d->lzma->stream.avail_in = Size;
1569 d->lzma->stream.next_out = d->lzma->buffer;
1570 d->lzma->stream.avail_out = sizeof(d->lzma->buffer)/sizeof(d->lzma->buffer[0]);
1571 d->lzma->err = lzma_code(&d->lzma->stream, LZMA_RUN);
1572 if (d->lzma->err != LZMA_OK)
1573 return false;
1574 size_t const n = sizeof(d->lzma->buffer)/sizeof(d->lzma->buffer[0]) - d->lzma->stream.avail_out;
1575 size_t const m = (n == 0) ? 0 : fwrite(d->lzma->buffer, 1, n, d->lzma->file);
1576 if (m != n)
1577 Res = -1;
1578 else
1579 Res = Size - d->lzma->stream.avail_in;
1580 }
699b209e 1581#endif
7f350a37
DK
1582 else
1583 Res = write(iFd,From,Size);
1584
b0db36b1
AL
1585 if (Res < 0 && errno == EINTR)
1586 continue;
1587 if (Res < 0)
1588 {
7f350a37
DK
1589 if (false)
1590 /* dummy so that the rest can be 'else if's */;
c4997486 1591#ifdef HAVE_ZLIB
7f350a37 1592 else if (d != NULL && d->gz != NULL)
c4997486
DK
1593 {
1594 int err;
1595 char const * const errmsg = gzerror(d->gz, &err);
1596 if (err != Z_ERRNO)
ae635e3c 1597 return FileFdError("gzwrite: %s (%d: %s)", _("Write error"), err, errmsg);
c4997486
DK
1598 }
1599#endif
1600#ifdef HAVE_BZ2
7f350a37 1601 else if (d != NULL && d->bz2 != NULL)
c4997486
DK
1602 {
1603 int err;
1604 char const * const errmsg = BZ2_bzerror(d->bz2, &err);
1605 if (err != BZ_IO_ERROR)
ae635e3c 1606 return FileFdError("BZ2_bzwrite: %s (%d: %s)", _("Write error"), err, errmsg);
c4997486 1607 }
7f350a37
DK
1608#endif
1609#ifdef HAVE_LZMA
1610 else if (d != NULL && d->lzma != NULL)
1611 return FileFdErrno("lzma_fwrite", _("Write error"));
c4997486 1612#endif
ae635e3c 1613 return FileFdErrno("write",_("Write error"));
b0db36b1
AL
1614 }
1615
cf4ff3b7 1616 From = (char const *)From + Res;
b0db36b1 1617 Size -= Res;
ff477ee1
DK
1618 if (d != NULL)
1619 d->seekpos += Res;
578bfd0a 1620 }
b0db36b1 1621 while (Res > 0 && Size > 0);
578bfd0a 1622
b0db36b1
AL
1623 if (Size == 0)
1624 return true;
ae635e3c
DK
1625
1626 return FileFdError(_("write, still have %llu to write but couldn't"), Size);
d68d65ad
DK
1627}
1628bool FileFd::Write(int Fd, const void *From, unsigned long long Size)
1629{
3286ad13 1630 ssize_t Res;
d68d65ad
DK
1631 errno = 0;
1632 do
1633 {
1634 Res = write(Fd,From,Size);
1635 if (Res < 0 && errno == EINTR)
1636 continue;
1637 if (Res < 0)
1638 return _error->Errno("write",_("Write error"));
1639
cf4ff3b7 1640 From = (char const *)From + Res;
d68d65ad
DK
1641 Size -= Res;
1642 }
1643 while (Res > 0 && Size > 0);
1644
1645 if (Size == 0)
1646 return true;
1647
1648 return _error->Error(_("write, still have %llu to write but couldn't"), Size);
578bfd0a
AL
1649}
1650 /*}}}*/
8e06abb2 1651// FileFd::Seek - Seek in the file /*{{{*/
578bfd0a
AL
1652// ---------------------------------------------------------------------
1653/* */
650faab0 1654bool FileFd::Seek(unsigned long long To)
578bfd0a 1655{
4239dbca 1656 if (d != NULL && (d->pipe == true || d->InternalStream() == true))
699b209e 1657 {
1abbc47c
DK
1658 // Our poor man seeking in pipes is costly, so try to avoid it
1659 unsigned long long seekpos = Tell();
1660 if (seekpos == To)
1661 return true;
1662 else if (seekpos < To)
1663 return Skip(To - seekpos);
1664
561f860a 1665 if ((d->openmode & ReadOnly) != ReadOnly)
ae635e3c 1666 return FileFdError("Reopen is only implemented for read-only files!");
4239dbca 1667 d->InternalClose(FileName);
afb093cd
DK
1668 if (iFd != -1)
1669 close(iFd);
1670 iFd = -1;
561f860a
DK
1671 if (TemporaryFileName.empty() == false)
1672 iFd = open(TemporaryFileName.c_str(), O_RDONLY);
1673 else if (FileName.empty() == false)
1674 iFd = open(FileName.c_str(), O_RDONLY);
1675 else
6fd947bd
DK
1676 {
1677 if (d->compressed_fd > 0)
1678 if (lseek(d->compressed_fd, 0, SEEK_SET) != 0)
1679 iFd = d->compressed_fd;
500400fe 1680 if (iFd < 0)
ae635e3c 1681 return FileFdError("Reopen is not implemented for pipes opened with FileFd::OpenDescriptor()!");
6fd947bd 1682 }
561f860a
DK
1683
1684 if (OpenInternDescriptor(d->openmode, d->compressor) == false)
ae635e3c 1685 return FileFdError("Seek on file %s because it couldn't be reopened", FileName.c_str());
561f860a
DK
1686
1687 if (To != 0)
1688 return Skip(To);
1abbc47c
DK
1689
1690 d->seekpos = To;
561f860a 1691 return true;
699b209e 1692 }
3286ad13 1693 off_t res;
7efb8c8e 1694#ifdef HAVE_ZLIB
ff477ee1 1695 if (d != NULL && d->gz)
032bd56f 1696 res = gzseek(d->gz,To,SEEK_SET);
a3a03f5d 1697 else
699b209e 1698#endif
a3a03f5d 1699 res = lseek(iFd,To,SEEK_SET);
3286ad13 1700 if (res != (off_t)To)
ae635e3c 1701 return FileFdError("Unable to seek to %llu", To);
1abbc47c 1702
ff477ee1
DK
1703 if (d != NULL)
1704 d->seekpos = To;
727f18af
AL
1705 return true;
1706}
1707 /*}}}*/
1708// FileFd::Skip - Seek in the file /*{{{*/
1709// ---------------------------------------------------------------------
1710/* */
650faab0 1711bool FileFd::Skip(unsigned long long Over)
727f18af 1712{
4239dbca 1713 if (d != NULL && (d->pipe == true || d->InternalStream() == true))
40468850 1714 {
40468850
DK
1715 char buffer[1024];
1716 while (Over != 0)
1717 {
1718 unsigned long long toread = std::min((unsigned long long) sizeof(buffer), Over);
1719 if (Read(buffer, toread) == false)
ae635e3c 1720 return FileFdError("Unable to seek ahead %llu",Over);
40468850
DK
1721 Over -= toread;
1722 }
1723 return true;
1724 }
1725
3286ad13 1726 off_t res;
7efb8c8e 1727#ifdef HAVE_ZLIB
ff477ee1 1728 if (d != NULL && d->gz != NULL)
032bd56f 1729 res = gzseek(d->gz,Over,SEEK_CUR);
a3a03f5d 1730 else
699b209e 1731#endif
a3a03f5d 1732 res = lseek(iFd,Over,SEEK_CUR);
1733 if (res < 0)
ae635e3c 1734 return FileFdError("Unable to seek ahead %llu",Over);
ff477ee1
DK
1735 if (d != NULL)
1736 d->seekpos = res;
1abbc47c 1737
6d5dd02a
AL
1738 return true;
1739}
1740 /*}}}*/
1741// FileFd::Truncate - Truncate the file /*{{{*/
1742// ---------------------------------------------------------------------
1743/* */
650faab0 1744bool FileFd::Truncate(unsigned long long To)
6d5dd02a 1745{
ad5051ef
DK
1746 // truncating /dev/null is always successful - as we get an error otherwise
1747 if (To == 0 && FileName == "/dev/null")
1748 return true;
7f350a37 1749#if defined HAVE_ZLIB || defined HAVE_BZ2 || defined HAVE_LZMA
4239dbca 1750 if (d != NULL && (d->InternalStream() == true
7f350a37 1751#ifdef HAVE_ZLIB
4239dbca 1752 || d->gz != NULL
7f350a37 1753#endif
4239dbca 1754 ))
ae635e3c 1755 return FileFdError("Truncating compressed files is not implemented (%s)", FileName.c_str());
c4997486 1756#endif
6d5dd02a 1757 if (ftruncate(iFd,To) != 0)
ae635e3c
DK
1758 return FileFdError("Unable to truncate to %llu",To);
1759
578bfd0a
AL
1760 return true;
1761}
1762 /*}}}*/
7f25bdff
AL
1763// FileFd::Tell - Current seek position /*{{{*/
1764// ---------------------------------------------------------------------
1765/* */
650faab0 1766unsigned long long FileFd::Tell()
7f25bdff 1767{
1abbc47c
DK
1768 // In theory, we could just return seekpos here always instead of
1769 // seeking around, but not all users of FileFd use always Seek() and co
1770 // so d->seekpos isn't always true and we can just use it as a hint if
1771 // we have nothing else, but not always as an authority…
4239dbca 1772 if (d != NULL && (d->pipe == true || d->InternalStream() == true))
1abbc47c
DK
1773 return d->seekpos;
1774
a3a03f5d 1775 off_t Res;
7efb8c8e 1776#ifdef HAVE_ZLIB
ff477ee1 1777 if (d != NULL && d->gz != NULL)
032bd56f 1778 Res = gztell(d->gz);
a3a03f5d 1779 else
699b209e 1780#endif
a3a03f5d 1781 Res = lseek(iFd,0,SEEK_CUR);
7f25bdff 1782 if (Res == (off_t)-1)
ae635e3c 1783 FileFdErrno("lseek","Failed to determine the current file position");
ff477ee1
DK
1784 if (d != NULL)
1785 d->seekpos = Res;
7f25bdff
AL
1786 return Res;
1787}
1788 /*}}}*/
8190b07a 1789static bool StatFileFd(char const * const msg, int const iFd, std::string const &FileName, struct stat &Buf, FileFdPrivate * const d) /*{{{*/
578bfd0a 1790{
6008b79a
DK
1791 bool ispipe = (d != NULL && d->pipe == true);
1792 if (ispipe == false)
1793 {
1794 if (fstat(iFd,&Buf) != 0)
8190b07a
DK
1795 // higher-level code will generate more meaningful messages,
1796 // even translated this would be meaningless for users
1797 return _error->Errno("fstat", "Unable to determine %s for fd %i", msg, iFd);
6008b79a
DK
1798 ispipe = S_ISFIFO(Buf.st_mode);
1799 }
699b209e
DK
1800
1801 // for compressor pipes st_size is undefined and at 'best' zero
6008b79a 1802 if (ispipe == true)
699b209e
DK
1803 {
1804 // we set it here, too, as we get the info here for free
1805 // in theory the Open-methods should take care of it already
ff477ee1
DK
1806 if (d != NULL)
1807 d->pipe = true;
699b209e 1808 if (stat(FileName.c_str(), &Buf) != 0)
8190b07a
DK
1809 return _error->Errno("fstat", "Unable to determine %s for file %s", msg, FileName.c_str());
1810 }
1811 return true;
1812}
1813 /*}}}*/
1814// FileFd::FileSize - Return the size of the file /*{{{*/
1815unsigned long long FileFd::FileSize()
1816{
1817 struct stat Buf;
1818 if (StatFileFd("file size", iFd, FileName, Buf, d) == false)
1819 {
1820 Flags |= Fail;
1821 return 0;
699b209e 1822 }
4260fd39
DK
1823 return Buf.st_size;
1824}
1825 /*}}}*/
8190b07a
DK
1826// FileFd::ModificationTime - Return the time of last touch /*{{{*/
1827time_t FileFd::ModificationTime()
1828{
1829 struct stat Buf;
1830 if (StatFileFd("modification time", iFd, FileName, Buf, d) == false)
1831 {
1832 Flags |= Fail;
1833 return 0;
1834 }
1835 return Buf.st_mtime;
1836}
1837 /*}}}*/
4260fd39
DK
1838// FileFd::Size - Return the size of the content in the file /*{{{*/
1839// ---------------------------------------------------------------------
1840/* */
650faab0 1841unsigned long long FileFd::Size()
4260fd39 1842{
650faab0 1843 unsigned long long size = FileSize();
44dc669e 1844
699b209e
DK
1845 // for compressor pipes st_size is undefined and at 'best' zero,
1846 // so we 'read' the content and 'seek' back - see there
4239dbca 1847 if (d != NULL && (d->pipe == true || (d->InternalStream() == true && size > 0)))
699b209e 1848 {
1abbc47c 1849 unsigned long long const oldSeek = Tell();
699b209e
DK
1850 char ignore[1000];
1851 unsigned long long read = 0;
1852 do {
638593bd
DK
1853 if (Read(ignore, sizeof(ignore), &read) == false)
1854 {
1855 Seek(oldSeek);
1856 return 0;
1857 }
699b209e 1858 } while(read != 0);
1abbc47c
DK
1859 size = Tell();
1860 Seek(oldSeek);
699b209e 1861 }
7efb8c8e 1862#ifdef HAVE_ZLIB
44dc669e 1863 // only check gzsize if we are actually a gzip file, just checking for
032bd56f 1864 // "gz" is not sufficient as uncompressed files could be opened with
44dc669e 1865 // gzopen in "direct" mode as well
ff477ee1 1866 else if (d != NULL && d->gz && !gzdirect(d->gz) && size > 0)
9c182afa 1867 {
65c72a4b 1868 off_t const oldPos = lseek(iFd,0,SEEK_CUR);
9c182afa
MP
1869 /* unfortunately zlib.h doesn't provide a gzsize(), so we have to do
1870 * this ourselves; the original (uncompressed) file size is the last 32
1871 * bits of the file */
650faab0 1872 // FIXME: Size for gz-files is limited by 32bit… no largefile support
9c182afa 1873 if (lseek(iFd, -4, SEEK_END) < 0)
638593bd
DK
1874 {
1875 FileFdErrno("lseek","Unable to seek to end of gzipped file");
1876 return 0;
1877 }
1878 size = 0;
9c182afa 1879 if (read(iFd, &size, 4) != 4)
638593bd
DK
1880 {
1881 FileFdErrno("read","Unable to read original size of gzipped file");
1882 return 0;
1883 }
f330c0f3
DK
1884
1885#ifdef WORDS_BIGENDIAN
2cae0ccb
DK
1886 uint32_t tmp_size = size;
1887 uint8_t const * const p = (uint8_t const * const) &tmp_size;
1888 tmp_size = (p[3] << 24) | (p[2] << 16) | (p[1] << 8) | p[0];
1889 size = tmp_size;
f330c0f3 1890#endif
9c182afa 1891
65c72a4b 1892 if (lseek(iFd, oldPos, SEEK_SET) < 0)
638593bd
DK
1893 {
1894 FileFdErrno("lseek","Unable to seek in gzipped file");
1895 return 0;
1896 }
65c72a4b 1897
9c182afa
MP
1898 return size;
1899 }
699b209e 1900#endif
9c182afa 1901
44dc669e 1902 return size;
578bfd0a
AL
1903}
1904 /*}}}*/
8e06abb2 1905// FileFd::Close - Close the file if the close flag is set /*{{{*/
578bfd0a
AL
1906// ---------------------------------------------------------------------
1907/* */
8e06abb2 1908bool FileFd::Close()
578bfd0a 1909{
032bd56f
DK
1910 if (iFd == -1)
1911 return true;
1912
578bfd0a
AL
1913 bool Res = true;
1914 if ((Flags & AutoClose) == AutoClose)
d13c2d3f 1915 {
500400fe
DK
1916 if ((Flags & Compressed) != Compressed && iFd > 0 && close(iFd) != 0)
1917 Res &= _error->Errno("close",_("Problem closing the file %s"), FileName.c_str());
1918
1919 if (d != NULL)
1920 {
1921 Res &= d->CloseDown(FileName);
1922 delete d;
1923 d = NULL;
1924 }
d13c2d3f 1925 }
3010fb0e 1926
d3aac32e 1927 if ((Flags & Replace) == Replace) {
3010fb0e 1928 if (rename(TemporaryFileName.c_str(), FileName.c_str()) != 0)
62d073d9
DK
1929 Res &= _error->Errno("rename",_("Problem renaming the file %s to %s"), TemporaryFileName.c_str(), FileName.c_str());
1930
fd3b761e 1931 FileName = TemporaryFileName; // for the unlink() below.
257e8d66 1932 TemporaryFileName.clear();
3010fb0e 1933 }
62d073d9
DK
1934
1935 iFd = -1;
1936
578bfd0a
AL
1937 if ((Flags & Fail) == Fail && (Flags & DelOnFail) == DelOnFail &&
1938 FileName.empty() == false)
1939 if (unlink(FileName.c_str()) != 0)
62d073d9 1940 Res &= _error->WarningE("unlnk",_("Problem unlinking the file %s"), FileName.c_str());
3010fb0e 1941
fbb89d94
DK
1942 if (Res == false)
1943 Flags |= Fail;
578bfd0a
AL
1944 return Res;
1945}
1946 /*}}}*/
b2e465d6
AL
1947// FileFd::Sync - Sync the file /*{{{*/
1948// ---------------------------------------------------------------------
1949/* */
1950bool FileFd::Sync()
1951{
b2e465d6 1952 if (fsync(iFd) != 0)
ae635e3c
DK
1953 return FileFdErrno("sync",_("Problem syncing the file"));
1954 return true;
1955}
1956 /*}}}*/
1957// FileFd::FileFdErrno - set Fail and call _error->Errno *{{{*/
1958bool FileFd::FileFdErrno(const char *Function, const char *Description,...)
1959{
1960 Flags |= Fail;
1961 va_list args;
1962 size_t msgSize = 400;
1963 int const errsv = errno;
1964 while (true)
fbb89d94 1965 {
ae635e3c
DK
1966 va_start(args,Description);
1967 if (_error->InsertErrno(GlobalError::ERROR, Function, Description, args, errsv, msgSize) == false)
1968 break;
1969 va_end(args);
fbb89d94 1970 }
ae635e3c
DK
1971 return false;
1972}
1973 /*}}}*/
1974// FileFd::FileFdError - set Fail and call _error->Error *{{{*/
1975bool FileFd::FileFdError(const char *Description,...) {
1976 Flags |= Fail;
1977 va_list args;
1978 size_t msgSize = 400;
1979 while (true)
1980 {
1981 va_start(args,Description);
1982 if (_error->Insert(GlobalError::ERROR, Description, args, msgSize) == false)
1983 break;
1984 va_end(args);
1985 }
1986 return false;
b2e465d6
AL
1987}
1988 /*}}}*/
699b209e 1989
7f350a37
DK
1990APT_DEPRECATED gzFile FileFd::gzFd() {
1991#ifdef HAVE_ZLIB
1992 return d->gz;
1993#else
1994 return NULL;
1995#endif
1996}
488011fa
MV
1997
1998
1999// Glob - wrapper around "glob()" /*{{{*/
2000// ---------------------------------------------------------------------
2001/* */
2002std::vector<std::string> Glob(std::string const &pattern, int flags)
2003{
2004 std::vector<std::string> result;
2005 glob_t globbuf;
ec4835a1
ÁGM
2006 int glob_res;
2007 unsigned int i;
488011fa
MV
2008
2009 glob_res = glob(pattern.c_str(), flags, NULL, &globbuf);
2010
2011 if (glob_res != 0)
2012 {
2013 if(glob_res != GLOB_NOMATCH) {
2014 _error->Errno("glob", "Problem with glob");
2015 return result;
2016 }
2017 }
2018
2019 // append results
2020 for(i=0;i<globbuf.gl_pathc;i++)
2021 result.push_back(string(globbuf.gl_pathv[i]));
2022
2023 globfree(&globbuf);
2024 return result;
2025}
2026 /*}}}*/
68e01721
MV
2027
2028std::string GetTempDir()
2029{
2030 const char *tmpdir = getenv("TMPDIR");
2031
2032#ifdef P_tmpdir
2033 if (!tmpdir)
2034 tmpdir = P_tmpdir;
2035#endif
2036
2037 // check that tmpdir is set and exists
2038 struct stat st;
a077861a 2039 if (!tmpdir || strlen(tmpdir) == 0 || stat(tmpdir, &st) != 0)
68e01721
MV
2040 tmpdir = "/tmp";
2041
2042 return string(tmpdir);
2043}
c1409d1b
MV
2044
2045bool Rename(std::string From, std::string To)
2046{
2047 if (rename(From.c_str(),To.c_str()) != 0)
2048 {
2049 _error->Error(_("rename failed, %s (%s -> %s)."),strerror(errno),
2050 From.c_str(),To.c_str());
2051 return false;
2052 }
2053 return true;
2054}