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