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