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