]> git.saurik.com Git - apt.git/blame - apt-pkg/contrib/fileutl.cc
Updated German doc translation
[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
69d6988a 1194 if (AutoClose == false && 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
1433 Res = Size - d->lzma->stream.avail_out;
1434 }
1435#endif
1436 else
a3a03f5d 1437 Res = read(iFd,To,Size);
b711c01e 1438
b0db36b1
AL
1439 if (Res < 0)
1440 {
b711c01e
DK
1441 if (errno == EINTR)
1442 continue;
7f350a37
DK
1443 if (false)
1444 /* dummy so that the rest can be 'else if's */;
7efb8c8e 1445#ifdef HAVE_ZLIB
7f350a37 1446 else if (d != NULL && d->gz != NULL)
b711c01e
DK
1447 {
1448 int err;
1449 char const * const errmsg = gzerror(d->gz, &err);
1450 if (err != Z_ERRNO)
ae635e3c 1451 return FileFdError("gzread: %s (%d: %s)", _("Read error"), err, errmsg);
b711c01e 1452 }
c4997486
DK
1453#endif
1454#ifdef HAVE_BZ2
7f350a37 1455 else if (d != NULL && d->bz2 != NULL)
c4997486
DK
1456 {
1457 int err;
1458 char const * const errmsg = BZ2_bzerror(d->bz2, &err);
1459 if (err != BZ_IO_ERROR)
ae635e3c 1460 return FileFdError("BZ2_bzread: %s (%d: %s)", _("Read error"), err, errmsg);
c4997486 1461 }
7f350a37
DK
1462#endif
1463#ifdef HAVE_LZMA
1464 else if (d != NULL && d->lzma != NULL)
1465 return FileFdError("lzma_read: %s (%d)", _("Read error"), d->lzma->err);
b711c01e 1466#endif
ae635e3c 1467 return FileFdErrno("read",_("Read error"));
b0db36b1 1468 }
578bfd0a 1469
b0db36b1
AL
1470 To = (char *)To + Res;
1471 Size -= Res;
ff477ee1
DK
1472 if (d != NULL)
1473 d->seekpos += Res;
f604cf55
AL
1474 if (Actual != 0)
1475 *Actual += Res;
b0db36b1
AL
1476 }
1477 while (Res > 0 && Size > 0);
1478
1479 if (Size == 0)
1480 return true;
1481
ddc1d8d0 1482 // Eof handling
f604cf55 1483 if (Actual != 0)
ddc1d8d0
AL
1484 {
1485 Flags |= HitEof;
1486 return true;
1487 }
ae635e3c
DK
1488
1489 return FileFdError(_("read, still have %llu to read but none left"), Size);
578bfd0a
AL
1490}
1491 /*}}}*/
032bd56f
DK
1492// FileFd::ReadLine - Read a complete line from the file /*{{{*/
1493// ---------------------------------------------------------------------
1494/* Beware: This method can be quiet slow for big buffers on UNcompressed
1495 files because of the naive implementation! */
1496char* FileFd::ReadLine(char *To, unsigned long long const Size)
1497{
699b209e 1498 *To = '\0';
7efb8c8e 1499#ifdef HAVE_ZLIB
ff477ee1 1500 if (d != NULL && d->gz != NULL)
032bd56f 1501 return gzgets(d->gz, To, Size);
699b209e 1502#endif
032bd56f
DK
1503
1504 unsigned long long read = 0;
40468850
DK
1505 while ((Size - 1) != read)
1506 {
1507 unsigned long long done = 0;
1508 if (Read(To + read, 1, &done) == false)
1509 return NULL;
1510 if (done == 0)
1511 break;
1512 if (To[read++] == '\n')
1513 break;
1514 }
1515 if (read == 0)
032bd56f 1516 return NULL;
40468850 1517 To[read] = '\0';
032bd56f
DK
1518 return To;
1519}
1520 /*}}}*/
8e06abb2 1521// FileFd::Write - Write to the file /*{{{*/
578bfd0a
AL
1522// ---------------------------------------------------------------------
1523/* */
650faab0 1524bool FileFd::Write(const void *From,unsigned long long Size)
578bfd0a 1525{
3286ad13 1526 ssize_t Res;
b0db36b1
AL
1527 errno = 0;
1528 do
578bfd0a 1529 {
7f350a37
DK
1530 if (false)
1531 /* dummy so that the rest can be 'else if's */;
7efb8c8e 1532#ifdef HAVE_ZLIB
7f350a37
DK
1533 else if (d != NULL && d->gz != NULL)
1534 Res = gzwrite(d->gz,From,Size);
c4997486
DK
1535#endif
1536#ifdef HAVE_BZ2
7f350a37
DK
1537 else if (d != NULL && d->bz2 != NULL)
1538 Res = BZ2_bzwrite(d->bz2,(void*)From,Size);
699b209e 1539#endif
7f350a37
DK
1540#ifdef HAVE_LZMA
1541 else if (d != NULL && d->lzma != NULL)
1542 {
1543 d->lzma->stream.next_in = (uint8_t *)From;
1544 d->lzma->stream.avail_in = Size;
1545 d->lzma->stream.next_out = d->lzma->buffer;
1546 d->lzma->stream.avail_out = sizeof(d->lzma->buffer)/sizeof(d->lzma->buffer[0]);
1547 d->lzma->err = lzma_code(&d->lzma->stream, LZMA_RUN);
1548 if (d->lzma->err != LZMA_OK)
1549 return false;
1550 size_t const n = sizeof(d->lzma->buffer)/sizeof(d->lzma->buffer[0]) - d->lzma->stream.avail_out;
1551 size_t const m = (n == 0) ? 0 : fwrite(d->lzma->buffer, 1, n, d->lzma->file);
1552 if (m != n)
1553 Res = -1;
1554 else
1555 Res = Size - d->lzma->stream.avail_in;
1556 }
699b209e 1557#endif
7f350a37
DK
1558 else
1559 Res = write(iFd,From,Size);
1560
b0db36b1
AL
1561 if (Res < 0 && errno == EINTR)
1562 continue;
1563 if (Res < 0)
1564 {
7f350a37
DK
1565 if (false)
1566 /* dummy so that the rest can be 'else if's */;
c4997486 1567#ifdef HAVE_ZLIB
7f350a37 1568 else if (d != NULL && d->gz != NULL)
c4997486
DK
1569 {
1570 int err;
1571 char const * const errmsg = gzerror(d->gz, &err);
1572 if (err != Z_ERRNO)
ae635e3c 1573 return FileFdError("gzwrite: %s (%d: %s)", _("Write error"), err, errmsg);
c4997486
DK
1574 }
1575#endif
1576#ifdef HAVE_BZ2
7f350a37 1577 else if (d != NULL && d->bz2 != NULL)
c4997486
DK
1578 {
1579 int err;
1580 char const * const errmsg = BZ2_bzerror(d->bz2, &err);
1581 if (err != BZ_IO_ERROR)
ae635e3c 1582 return FileFdError("BZ2_bzwrite: %s (%d: %s)", _("Write error"), err, errmsg);
c4997486 1583 }
7f350a37
DK
1584#endif
1585#ifdef HAVE_LZMA
1586 else if (d != NULL && d->lzma != NULL)
1587 return FileFdErrno("lzma_fwrite", _("Write error"));
c4997486 1588#endif
ae635e3c 1589 return FileFdErrno("write",_("Write error"));
b0db36b1
AL
1590 }
1591
cf4ff3b7 1592 From = (char const *)From + Res;
b0db36b1 1593 Size -= Res;
ff477ee1
DK
1594 if (d != NULL)
1595 d->seekpos += Res;
578bfd0a 1596 }
b0db36b1 1597 while (Res > 0 && Size > 0);
578bfd0a 1598
b0db36b1
AL
1599 if (Size == 0)
1600 return true;
ae635e3c
DK
1601
1602 return FileFdError(_("write, still have %llu to write but couldn't"), Size);
d68d65ad
DK
1603}
1604bool FileFd::Write(int Fd, const void *From, unsigned long long Size)
1605{
3286ad13 1606 ssize_t Res;
d68d65ad
DK
1607 errno = 0;
1608 do
1609 {
1610 Res = write(Fd,From,Size);
1611 if (Res < 0 && errno == EINTR)
1612 continue;
1613 if (Res < 0)
1614 return _error->Errno("write",_("Write error"));
1615
cf4ff3b7 1616 From = (char const *)From + Res;
d68d65ad
DK
1617 Size -= Res;
1618 }
1619 while (Res > 0 && Size > 0);
1620
1621 if (Size == 0)
1622 return true;
1623
1624 return _error->Error(_("write, still have %llu to write but couldn't"), Size);
578bfd0a
AL
1625}
1626 /*}}}*/
8e06abb2 1627// FileFd::Seek - Seek in the file /*{{{*/
578bfd0a
AL
1628// ---------------------------------------------------------------------
1629/* */
650faab0 1630bool FileFd::Seek(unsigned long long To)
578bfd0a 1631{
4239dbca 1632 if (d != NULL && (d->pipe == true || d->InternalStream() == true))
699b209e 1633 {
1abbc47c
DK
1634 // Our poor man seeking in pipes is costly, so try to avoid it
1635 unsigned long long seekpos = Tell();
1636 if (seekpos == To)
1637 return true;
1638 else if (seekpos < To)
1639 return Skip(To - seekpos);
1640
561f860a 1641 if ((d->openmode & ReadOnly) != ReadOnly)
ae635e3c 1642 return FileFdError("Reopen is only implemented for read-only files!");
4239dbca 1643 d->InternalClose(FileName);
afb093cd
DK
1644 if (iFd != -1)
1645 close(iFd);
1646 iFd = -1;
561f860a
DK
1647 if (TemporaryFileName.empty() == false)
1648 iFd = open(TemporaryFileName.c_str(), O_RDONLY);
1649 else if (FileName.empty() == false)
1650 iFd = open(FileName.c_str(), O_RDONLY);
1651 else
6fd947bd
DK
1652 {
1653 if (d->compressed_fd > 0)
1654 if (lseek(d->compressed_fd, 0, SEEK_SET) != 0)
1655 iFd = d->compressed_fd;
500400fe 1656 if (iFd < 0)
ae635e3c 1657 return FileFdError("Reopen is not implemented for pipes opened with FileFd::OpenDescriptor()!");
6fd947bd 1658 }
561f860a
DK
1659
1660 if (OpenInternDescriptor(d->openmode, d->compressor) == false)
ae635e3c 1661 return FileFdError("Seek on file %s because it couldn't be reopened", FileName.c_str());
561f860a
DK
1662
1663 if (To != 0)
1664 return Skip(To);
1abbc47c
DK
1665
1666 d->seekpos = To;
561f860a 1667 return true;
699b209e 1668 }
3286ad13 1669 off_t res;
7efb8c8e 1670#ifdef HAVE_ZLIB
ff477ee1 1671 if (d != NULL && d->gz)
032bd56f 1672 res = gzseek(d->gz,To,SEEK_SET);
a3a03f5d 1673 else
699b209e 1674#endif
a3a03f5d 1675 res = lseek(iFd,To,SEEK_SET);
3286ad13 1676 if (res != (off_t)To)
ae635e3c 1677 return FileFdError("Unable to seek to %llu", To);
1abbc47c 1678
ff477ee1
DK
1679 if (d != NULL)
1680 d->seekpos = To;
727f18af
AL
1681 return true;
1682}
1683 /*}}}*/
1684// FileFd::Skip - Seek in the file /*{{{*/
1685// ---------------------------------------------------------------------
1686/* */
650faab0 1687bool FileFd::Skip(unsigned long long Over)
727f18af 1688{
4239dbca 1689 if (d != NULL && (d->pipe == true || d->InternalStream() == true))
40468850
DK
1690 {
1691 d->seekpos += Over;
1692 char buffer[1024];
1693 while (Over != 0)
1694 {
1695 unsigned long long toread = std::min((unsigned long long) sizeof(buffer), Over);
1696 if (Read(buffer, toread) == false)
ae635e3c 1697 return FileFdError("Unable to seek ahead %llu",Over);
40468850
DK
1698 Over -= toread;
1699 }
1700 return true;
1701 }
1702
3286ad13 1703 off_t res;
7efb8c8e 1704#ifdef HAVE_ZLIB
ff477ee1 1705 if (d != NULL && d->gz != NULL)
032bd56f 1706 res = gzseek(d->gz,Over,SEEK_CUR);
a3a03f5d 1707 else
699b209e 1708#endif
a3a03f5d 1709 res = lseek(iFd,Over,SEEK_CUR);
1710 if (res < 0)
ae635e3c 1711 return FileFdError("Unable to seek ahead %llu",Over);
ff477ee1
DK
1712 if (d != NULL)
1713 d->seekpos = res;
1abbc47c 1714
6d5dd02a
AL
1715 return true;
1716}
1717 /*}}}*/
1718// FileFd::Truncate - Truncate the file /*{{{*/
1719// ---------------------------------------------------------------------
1720/* */
650faab0 1721bool FileFd::Truncate(unsigned long long To)
6d5dd02a 1722{
ad5051ef
DK
1723 // truncating /dev/null is always successful - as we get an error otherwise
1724 if (To == 0 && FileName == "/dev/null")
1725 return true;
7f350a37 1726#if defined HAVE_ZLIB || defined HAVE_BZ2 || defined HAVE_LZMA
4239dbca 1727 if (d != NULL && (d->InternalStream() == true
7f350a37 1728#ifdef HAVE_ZLIB
4239dbca 1729 || d->gz != NULL
7f350a37 1730#endif
4239dbca 1731 ))
ae635e3c 1732 return FileFdError("Truncating compressed files is not implemented (%s)", FileName.c_str());
c4997486 1733#endif
6d5dd02a 1734 if (ftruncate(iFd,To) != 0)
ae635e3c
DK
1735 return FileFdError("Unable to truncate to %llu",To);
1736
578bfd0a
AL
1737 return true;
1738}
1739 /*}}}*/
7f25bdff
AL
1740// FileFd::Tell - Current seek position /*{{{*/
1741// ---------------------------------------------------------------------
1742/* */
650faab0 1743unsigned long long FileFd::Tell()
7f25bdff 1744{
1abbc47c
DK
1745 // In theory, we could just return seekpos here always instead of
1746 // seeking around, but not all users of FileFd use always Seek() and co
1747 // so d->seekpos isn't always true and we can just use it as a hint if
1748 // we have nothing else, but not always as an authority…
4239dbca 1749 if (d != NULL && (d->pipe == true || d->InternalStream() == true))
1abbc47c
DK
1750 return d->seekpos;
1751
a3a03f5d 1752 off_t Res;
7efb8c8e 1753#ifdef HAVE_ZLIB
ff477ee1 1754 if (d != NULL && d->gz != NULL)
032bd56f 1755 Res = gztell(d->gz);
a3a03f5d 1756 else
699b209e 1757#endif
a3a03f5d 1758 Res = lseek(iFd,0,SEEK_CUR);
7f25bdff 1759 if (Res == (off_t)-1)
ae635e3c 1760 FileFdErrno("lseek","Failed to determine the current file position");
ff477ee1
DK
1761 if (d != NULL)
1762 d->seekpos = Res;
7f25bdff
AL
1763 return Res;
1764}
1765 /*}}}*/
8190b07a 1766static bool StatFileFd(char const * const msg, int const iFd, std::string const &FileName, struct stat &Buf, FileFdPrivate * const d) /*{{{*/
578bfd0a 1767{
6008b79a
DK
1768 bool ispipe = (d != NULL && d->pipe == true);
1769 if (ispipe == false)
1770 {
1771 if (fstat(iFd,&Buf) != 0)
8190b07a
DK
1772 // higher-level code will generate more meaningful messages,
1773 // even translated this would be meaningless for users
1774 return _error->Errno("fstat", "Unable to determine %s for fd %i", msg, iFd);
6008b79a
DK
1775 ispipe = S_ISFIFO(Buf.st_mode);
1776 }
699b209e
DK
1777
1778 // for compressor pipes st_size is undefined and at 'best' zero
6008b79a 1779 if (ispipe == true)
699b209e
DK
1780 {
1781 // we set it here, too, as we get the info here for free
1782 // in theory the Open-methods should take care of it already
ff477ee1
DK
1783 if (d != NULL)
1784 d->pipe = true;
699b209e 1785 if (stat(FileName.c_str(), &Buf) != 0)
8190b07a
DK
1786 return _error->Errno("fstat", "Unable to determine %s for file %s", msg, FileName.c_str());
1787 }
1788 return true;
1789}
1790 /*}}}*/
1791// FileFd::FileSize - Return the size of the file /*{{{*/
1792unsigned long long FileFd::FileSize()
1793{
1794 struct stat Buf;
1795 if (StatFileFd("file size", iFd, FileName, Buf, d) == false)
1796 {
1797 Flags |= Fail;
1798 return 0;
699b209e 1799 }
4260fd39
DK
1800 return Buf.st_size;
1801}
1802 /*}}}*/
8190b07a
DK
1803// FileFd::ModificationTime - Return the time of last touch /*{{{*/
1804time_t FileFd::ModificationTime()
1805{
1806 struct stat Buf;
1807 if (StatFileFd("modification time", iFd, FileName, Buf, d) == false)
1808 {
1809 Flags |= Fail;
1810 return 0;
1811 }
1812 return Buf.st_mtime;
1813}
1814 /*}}}*/
4260fd39
DK
1815// FileFd::Size - Return the size of the content in the file /*{{{*/
1816// ---------------------------------------------------------------------
1817/* */
650faab0 1818unsigned long long FileFd::Size()
4260fd39 1819{
650faab0 1820 unsigned long long size = FileSize();
44dc669e 1821
699b209e
DK
1822 // for compressor pipes st_size is undefined and at 'best' zero,
1823 // so we 'read' the content and 'seek' back - see there
4239dbca 1824 if (d != NULL && (d->pipe == true || (d->InternalStream() == true && size > 0)))
699b209e 1825 {
1abbc47c 1826 unsigned long long const oldSeek = Tell();
699b209e
DK
1827 char ignore[1000];
1828 unsigned long long read = 0;
1829 do {
638593bd
DK
1830 if (Read(ignore, sizeof(ignore), &read) == false)
1831 {
1832 Seek(oldSeek);
1833 return 0;
1834 }
699b209e 1835 } while(read != 0);
1abbc47c
DK
1836 size = Tell();
1837 Seek(oldSeek);
699b209e 1838 }
7efb8c8e 1839#ifdef HAVE_ZLIB
44dc669e 1840 // only check gzsize if we are actually a gzip file, just checking for
032bd56f 1841 // "gz" is not sufficient as uncompressed files could be opened with
44dc669e 1842 // gzopen in "direct" mode as well
ff477ee1 1843 else if (d != NULL && d->gz && !gzdirect(d->gz) && size > 0)
9c182afa 1844 {
65c72a4b 1845 off_t const oldPos = lseek(iFd,0,SEEK_CUR);
9c182afa
MP
1846 /* unfortunately zlib.h doesn't provide a gzsize(), so we have to do
1847 * this ourselves; the original (uncompressed) file size is the last 32
1848 * bits of the file */
650faab0 1849 // FIXME: Size for gz-files is limited by 32bit… no largefile support
9c182afa 1850 if (lseek(iFd, -4, SEEK_END) < 0)
638593bd
DK
1851 {
1852 FileFdErrno("lseek","Unable to seek to end of gzipped file");
1853 return 0;
1854 }
1855 size = 0;
9c182afa 1856 if (read(iFd, &size, 4) != 4)
638593bd
DK
1857 {
1858 FileFdErrno("read","Unable to read original size of gzipped file");
1859 return 0;
1860 }
f330c0f3
DK
1861
1862#ifdef WORDS_BIGENDIAN
2cae0ccb
DK
1863 uint32_t tmp_size = size;
1864 uint8_t const * const p = (uint8_t const * const) &tmp_size;
1865 tmp_size = (p[3] << 24) | (p[2] << 16) | (p[1] << 8) | p[0];
1866 size = tmp_size;
f330c0f3 1867#endif
9c182afa 1868
65c72a4b 1869 if (lseek(iFd, oldPos, SEEK_SET) < 0)
638593bd
DK
1870 {
1871 FileFdErrno("lseek","Unable to seek in gzipped file");
1872 return 0;
1873 }
65c72a4b 1874
9c182afa
MP
1875 return size;
1876 }
699b209e 1877#endif
9c182afa 1878
44dc669e 1879 return size;
578bfd0a
AL
1880}
1881 /*}}}*/
8e06abb2 1882// FileFd::Close - Close the file if the close flag is set /*{{{*/
578bfd0a
AL
1883// ---------------------------------------------------------------------
1884/* */
8e06abb2 1885bool FileFd::Close()
578bfd0a 1886{
032bd56f
DK
1887 if (iFd == -1)
1888 return true;
1889
578bfd0a
AL
1890 bool Res = true;
1891 if ((Flags & AutoClose) == AutoClose)
d13c2d3f 1892 {
500400fe
DK
1893 if ((Flags & Compressed) != Compressed && iFd > 0 && close(iFd) != 0)
1894 Res &= _error->Errno("close",_("Problem closing the file %s"), FileName.c_str());
1895
1896 if (d != NULL)
1897 {
1898 Res &= d->CloseDown(FileName);
1899 delete d;
1900 d = NULL;
1901 }
d13c2d3f 1902 }
3010fb0e 1903
d3aac32e 1904 if ((Flags & Replace) == Replace) {
3010fb0e 1905 if (rename(TemporaryFileName.c_str(), FileName.c_str()) != 0)
62d073d9
DK
1906 Res &= _error->Errno("rename",_("Problem renaming the file %s to %s"), TemporaryFileName.c_str(), FileName.c_str());
1907
fd3b761e 1908 FileName = TemporaryFileName; // for the unlink() below.
257e8d66 1909 TemporaryFileName.clear();
3010fb0e 1910 }
62d073d9
DK
1911
1912 iFd = -1;
1913
578bfd0a
AL
1914 if ((Flags & Fail) == Fail && (Flags & DelOnFail) == DelOnFail &&
1915 FileName.empty() == false)
1916 if (unlink(FileName.c_str()) != 0)
62d073d9 1917 Res &= _error->WarningE("unlnk",_("Problem unlinking the file %s"), FileName.c_str());
3010fb0e 1918
fbb89d94
DK
1919 if (Res == false)
1920 Flags |= Fail;
578bfd0a
AL
1921 return Res;
1922}
1923 /*}}}*/
b2e465d6
AL
1924// FileFd::Sync - Sync the file /*{{{*/
1925// ---------------------------------------------------------------------
1926/* */
1927bool FileFd::Sync()
1928{
b2e465d6 1929 if (fsync(iFd) != 0)
ae635e3c
DK
1930 return FileFdErrno("sync",_("Problem syncing the file"));
1931 return true;
1932}
1933 /*}}}*/
1934// FileFd::FileFdErrno - set Fail and call _error->Errno *{{{*/
1935bool FileFd::FileFdErrno(const char *Function, const char *Description,...)
1936{
1937 Flags |= Fail;
1938 va_list args;
1939 size_t msgSize = 400;
1940 int const errsv = errno;
1941 while (true)
fbb89d94 1942 {
ae635e3c
DK
1943 va_start(args,Description);
1944 if (_error->InsertErrno(GlobalError::ERROR, Function, Description, args, errsv, msgSize) == false)
1945 break;
1946 va_end(args);
fbb89d94 1947 }
ae635e3c
DK
1948 return false;
1949}
1950 /*}}}*/
1951// FileFd::FileFdError - set Fail and call _error->Error *{{{*/
1952bool FileFd::FileFdError(const char *Description,...) {
1953 Flags |= Fail;
1954 va_list args;
1955 size_t msgSize = 400;
1956 while (true)
1957 {
1958 va_start(args,Description);
1959 if (_error->Insert(GlobalError::ERROR, Description, args, msgSize) == false)
1960 break;
1961 va_end(args);
1962 }
1963 return false;
b2e465d6
AL
1964}
1965 /*}}}*/
699b209e 1966
7f350a37
DK
1967APT_DEPRECATED gzFile FileFd::gzFd() {
1968#ifdef HAVE_ZLIB
1969 return d->gz;
1970#else
1971 return NULL;
1972#endif
1973}
488011fa
MV
1974
1975
1976// Glob - wrapper around "glob()" /*{{{*/
1977// ---------------------------------------------------------------------
1978/* */
1979std::vector<std::string> Glob(std::string const &pattern, int flags)
1980{
1981 std::vector<std::string> result;
1982 glob_t globbuf;
ec4835a1
ÁGM
1983 int glob_res;
1984 unsigned int i;
488011fa
MV
1985
1986 glob_res = glob(pattern.c_str(), flags, NULL, &globbuf);
1987
1988 if (glob_res != 0)
1989 {
1990 if(glob_res != GLOB_NOMATCH) {
1991 _error->Errno("glob", "Problem with glob");
1992 return result;
1993 }
1994 }
1995
1996 // append results
1997 for(i=0;i<globbuf.gl_pathc;i++)
1998 result.push_back(string(globbuf.gl_pathv[i]));
1999
2000 globfree(&globbuf);
2001 return result;
2002}
2003 /*}}}*/
68e01721
MV
2004
2005std::string GetTempDir()
2006{
2007 const char *tmpdir = getenv("TMPDIR");
2008
2009#ifdef P_tmpdir
2010 if (!tmpdir)
2011 tmpdir = P_tmpdir;
2012#endif
2013
2014 // check that tmpdir is set and exists
2015 struct stat st;
a077861a 2016 if (!tmpdir || strlen(tmpdir) == 0 || stat(tmpdir, &st) != 0)
68e01721
MV
2017 tmpdir = "/tmp";
2018
2019 return string(tmpdir);
2020}
c1409d1b
MV
2021
2022bool Rename(std::string From, std::string To)
2023{
2024 if (rename(From.c_str(),To.c_str()) != 0)
2025 {
2026 _error->Error(_("rename failed, %s (%s -> %s)."),strerror(errno),
2027 From.c_str(),To.c_str());
2028 return false;
2029 }
2030 return true;
2031}