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