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