]> git.saurik.com Git - apt.git/blame - apt-pkg/contrib/fileutl.cc
always "delete d" in FileFd::~FileFd to coverity happy
[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 1220 if (d != NULL)
500400fe 1221 d->CloseDown(FileName);
96ab3c6f
MV
1222 delete d;
1223 d = NULL;
578bfd0a
AL
1224}
1225 /*}}}*/
8e06abb2 1226// FileFd::Read - Read a bit of the file /*{{{*/
578bfd0a 1227// ---------------------------------------------------------------------
b0db36b1
AL
1228/* We are carefull to handle interruption by a signal while reading
1229 gracefully. */
650faab0 1230bool FileFd::Read(void *To,unsigned long long Size,unsigned long long *Actual)
578bfd0a 1231{
b0db36b1
AL
1232 int Res;
1233 errno = 0;
f604cf55
AL
1234 if (Actual != 0)
1235 *Actual = 0;
699b209e 1236 *((char *)To) = '\0';
b0db36b1 1237 do
578bfd0a 1238 {
7efb8c8e 1239#ifdef HAVE_ZLIB
ff477ee1 1240 if (d != NULL && d->gz != NULL)
c4997486
DK
1241 Res = gzread(d->gz,To,Size);
1242 else
1243#endif
1244#ifdef HAVE_BZ2
ff477ee1 1245 if (d != NULL && d->bz2 != NULL)
c4997486 1246 Res = BZ2_bzread(d->bz2,To,Size);
a3a03f5d 1247 else
699b209e 1248#endif
a3a03f5d 1249 Res = read(iFd,To,Size);
b711c01e 1250
b0db36b1
AL
1251 if (Res < 0)
1252 {
b711c01e
DK
1253 if (errno == EINTR)
1254 continue;
7efb8c8e 1255#ifdef HAVE_ZLIB
ff477ee1 1256 if (d != NULL && d->gz != NULL)
b711c01e
DK
1257 {
1258 int err;
1259 char const * const errmsg = gzerror(d->gz, &err);
1260 if (err != Z_ERRNO)
ae635e3c 1261 return FileFdError("gzread: %s (%d: %s)", _("Read error"), err, errmsg);
b711c01e 1262 }
c4997486
DK
1263#endif
1264#ifdef HAVE_BZ2
ff477ee1 1265 if (d != NULL && d->bz2 != NULL)
c4997486
DK
1266 {
1267 int err;
1268 char const * const errmsg = BZ2_bzerror(d->bz2, &err);
1269 if (err != BZ_IO_ERROR)
ae635e3c 1270 return FileFdError("BZ2_bzread: %s (%d: %s)", _("Read error"), err, errmsg);
c4997486 1271 }
b711c01e 1272#endif
ae635e3c 1273 return FileFdErrno("read",_("Read error"));
b0db36b1 1274 }
578bfd0a 1275
b0db36b1
AL
1276 To = (char *)To + Res;
1277 Size -= Res;
ff477ee1
DK
1278 if (d != NULL)
1279 d->seekpos += Res;
f604cf55
AL
1280 if (Actual != 0)
1281 *Actual += Res;
b0db36b1
AL
1282 }
1283 while (Res > 0 && Size > 0);
1284
1285 if (Size == 0)
1286 return true;
1287
ddc1d8d0 1288 // Eof handling
f604cf55 1289 if (Actual != 0)
ddc1d8d0
AL
1290 {
1291 Flags |= HitEof;
1292 return true;
1293 }
ae635e3c
DK
1294
1295 return FileFdError(_("read, still have %llu to read but none left"), Size);
578bfd0a
AL
1296}
1297 /*}}}*/
032bd56f
DK
1298// FileFd::ReadLine - Read a complete line from the file /*{{{*/
1299// ---------------------------------------------------------------------
1300/* Beware: This method can be quiet slow for big buffers on UNcompressed
1301 files because of the naive implementation! */
1302char* FileFd::ReadLine(char *To, unsigned long long const Size)
1303{
699b209e 1304 *To = '\0';
7efb8c8e 1305#ifdef HAVE_ZLIB
ff477ee1 1306 if (d != NULL && d->gz != NULL)
032bd56f 1307 return gzgets(d->gz, To, Size);
699b209e 1308#endif
032bd56f
DK
1309
1310 unsigned long long read = 0;
40468850
DK
1311 while ((Size - 1) != read)
1312 {
1313 unsigned long long done = 0;
1314 if (Read(To + read, 1, &done) == false)
1315 return NULL;
1316 if (done == 0)
1317 break;
1318 if (To[read++] == '\n')
1319 break;
1320 }
1321 if (read == 0)
032bd56f 1322 return NULL;
40468850 1323 To[read] = '\0';
032bd56f
DK
1324 return To;
1325}
1326 /*}}}*/
8e06abb2 1327// FileFd::Write - Write to the file /*{{{*/
578bfd0a
AL
1328// ---------------------------------------------------------------------
1329/* */
650faab0 1330bool FileFd::Write(const void *From,unsigned long long Size)
578bfd0a 1331{
b0db36b1
AL
1332 int Res;
1333 errno = 0;
1334 do
578bfd0a 1335 {
7efb8c8e 1336#ifdef HAVE_ZLIB
ff477ee1 1337 if (d != NULL && d->gz != NULL)
032bd56f 1338 Res = gzwrite(d->gz,From,Size);
a3a03f5d 1339 else
c4997486
DK
1340#endif
1341#ifdef HAVE_BZ2
ff477ee1 1342 if (d != NULL && d->bz2 != NULL)
c4997486
DK
1343 Res = BZ2_bzwrite(d->bz2,(void*)From,Size);
1344 else
699b209e 1345#endif
a3a03f5d 1346 Res = write(iFd,From,Size);
b0db36b1
AL
1347 if (Res < 0 && errno == EINTR)
1348 continue;
1349 if (Res < 0)
1350 {
c4997486 1351#ifdef HAVE_ZLIB
ff477ee1 1352 if (d != NULL && d->gz != NULL)
c4997486
DK
1353 {
1354 int err;
1355 char const * const errmsg = gzerror(d->gz, &err);
1356 if (err != Z_ERRNO)
ae635e3c 1357 return FileFdError("gzwrite: %s (%d: %s)", _("Write error"), err, errmsg);
c4997486
DK
1358 }
1359#endif
1360#ifdef HAVE_BZ2
ff477ee1 1361 if (d != NULL && d->bz2 != NULL)
c4997486
DK
1362 {
1363 int err;
1364 char const * const errmsg = BZ2_bzerror(d->bz2, &err);
1365 if (err != BZ_IO_ERROR)
ae635e3c 1366 return FileFdError("BZ2_bzwrite: %s (%d: %s)", _("Write error"), err, errmsg);
c4997486
DK
1367 }
1368#endif
ae635e3c 1369 return FileFdErrno("write",_("Write error"));
b0db36b1
AL
1370 }
1371
1372 From = (char *)From + Res;
1373 Size -= Res;
ff477ee1
DK
1374 if (d != NULL)
1375 d->seekpos += Res;
578bfd0a 1376 }
b0db36b1 1377 while (Res > 0 && Size > 0);
578bfd0a 1378
b0db36b1
AL
1379 if (Size == 0)
1380 return true;
ae635e3c
DK
1381
1382 return FileFdError(_("write, still have %llu to write but couldn't"), Size);
d68d65ad
DK
1383}
1384bool FileFd::Write(int Fd, const void *From, unsigned long long Size)
1385{
1386 int Res;
1387 errno = 0;
1388 do
1389 {
1390 Res = write(Fd,From,Size);
1391 if (Res < 0 && errno == EINTR)
1392 continue;
1393 if (Res < 0)
1394 return _error->Errno("write",_("Write error"));
1395
1396 From = (char *)From + Res;
1397 Size -= Res;
1398 }
1399 while (Res > 0 && Size > 0);
1400
1401 if (Size == 0)
1402 return true;
1403
1404 return _error->Error(_("write, still have %llu to write but couldn't"), Size);
578bfd0a
AL
1405}
1406 /*}}}*/
8e06abb2 1407// FileFd::Seek - Seek in the file /*{{{*/
578bfd0a
AL
1408// ---------------------------------------------------------------------
1409/* */
650faab0 1410bool FileFd::Seek(unsigned long long To)
578bfd0a 1411{
ff477ee1 1412 if (d != NULL && (d->pipe == true
c4997486 1413#ifdef HAVE_BZ2
ff477ee1 1414 || d->bz2 != NULL
c4997486 1415#endif
ff477ee1 1416 ))
699b209e 1417 {
1abbc47c
DK
1418 // Our poor man seeking in pipes is costly, so try to avoid it
1419 unsigned long long seekpos = Tell();
1420 if (seekpos == To)
1421 return true;
1422 else if (seekpos < To)
1423 return Skip(To - seekpos);
1424
561f860a 1425 if ((d->openmode & ReadOnly) != ReadOnly)
ae635e3c 1426 return FileFdError("Reopen is only implemented for read-only files!");
c4997486 1427#ifdef HAVE_BZ2
e3b402f4
MV
1428 if (d->bz2 != NULL)
1429 {
1430 BZ2_bzclose(d->bz2);
1431 d->bz2 = NULL;
1432 }
c4997486 1433#endif
afb093cd
DK
1434 if (iFd != -1)
1435 close(iFd);
1436 iFd = -1;
561f860a
DK
1437 if (TemporaryFileName.empty() == false)
1438 iFd = open(TemporaryFileName.c_str(), O_RDONLY);
1439 else if (FileName.empty() == false)
1440 iFd = open(FileName.c_str(), O_RDONLY);
1441 else
6fd947bd
DK
1442 {
1443 if (d->compressed_fd > 0)
1444 if (lseek(d->compressed_fd, 0, SEEK_SET) != 0)
1445 iFd = d->compressed_fd;
500400fe 1446 if (iFd < 0)
ae635e3c 1447 return FileFdError("Reopen is not implemented for pipes opened with FileFd::OpenDescriptor()!");
6fd947bd 1448 }
561f860a
DK
1449
1450 if (OpenInternDescriptor(d->openmode, d->compressor) == false)
ae635e3c 1451 return FileFdError("Seek on file %s because it couldn't be reopened", FileName.c_str());
561f860a
DK
1452
1453 if (To != 0)
1454 return Skip(To);
1abbc47c
DK
1455
1456 d->seekpos = To;
561f860a 1457 return true;
699b209e 1458 }
a3a03f5d 1459 int res;
7efb8c8e 1460#ifdef HAVE_ZLIB
ff477ee1 1461 if (d != NULL && d->gz)
032bd56f 1462 res = gzseek(d->gz,To,SEEK_SET);
a3a03f5d 1463 else
699b209e 1464#endif
a3a03f5d 1465 res = lseek(iFd,To,SEEK_SET);
1466 if (res != (signed)To)
ae635e3c 1467 return FileFdError("Unable to seek to %llu", To);
1abbc47c 1468
ff477ee1
DK
1469 if (d != NULL)
1470 d->seekpos = To;
727f18af
AL
1471 return true;
1472}
1473 /*}}}*/
1474// FileFd::Skip - Seek in the file /*{{{*/
1475// ---------------------------------------------------------------------
1476/* */
650faab0 1477bool FileFd::Skip(unsigned long long Over)
727f18af 1478{
ff477ee1 1479 if (d != NULL && (d->pipe == true
c4997486 1480#ifdef HAVE_BZ2
ff477ee1 1481 || d->bz2 != NULL
c4997486 1482#endif
ff477ee1 1483 ))
40468850
DK
1484 {
1485 d->seekpos += Over;
1486 char buffer[1024];
1487 while (Over != 0)
1488 {
1489 unsigned long long toread = std::min((unsigned long long) sizeof(buffer), Over);
1490 if (Read(buffer, toread) == false)
ae635e3c 1491 return FileFdError("Unable to seek ahead %llu",Over);
40468850
DK
1492 Over -= toread;
1493 }
1494 return true;
1495 }
1496
a3a03f5d 1497 int res;
7efb8c8e 1498#ifdef HAVE_ZLIB
ff477ee1 1499 if (d != NULL && d->gz != NULL)
032bd56f 1500 res = gzseek(d->gz,Over,SEEK_CUR);
a3a03f5d 1501 else
699b209e 1502#endif
a3a03f5d 1503 res = lseek(iFd,Over,SEEK_CUR);
1504 if (res < 0)
ae635e3c 1505 return FileFdError("Unable to seek ahead %llu",Over);
ff477ee1
DK
1506 if (d != NULL)
1507 d->seekpos = res;
1abbc47c 1508
6d5dd02a
AL
1509 return true;
1510}
1511 /*}}}*/
1512// FileFd::Truncate - Truncate the file /*{{{*/
1513// ---------------------------------------------------------------------
1514/* */
650faab0 1515bool FileFd::Truncate(unsigned long long To)
6d5dd02a 1516{
c4997486 1517#if defined HAVE_ZLIB || defined HAVE_BZ2
ff477ee1 1518 if (d != NULL && (d->gz != NULL || d->bz2 != NULL))
ae635e3c 1519 return FileFdError("Truncating compressed files is not implemented (%s)", FileName.c_str());
c4997486 1520#endif
6d5dd02a 1521 if (ftruncate(iFd,To) != 0)
ae635e3c
DK
1522 return FileFdError("Unable to truncate to %llu",To);
1523
578bfd0a
AL
1524 return true;
1525}
1526 /*}}}*/
7f25bdff
AL
1527// FileFd::Tell - Current seek position /*{{{*/
1528// ---------------------------------------------------------------------
1529/* */
650faab0 1530unsigned long long FileFd::Tell()
7f25bdff 1531{
1abbc47c
DK
1532 // In theory, we could just return seekpos here always instead of
1533 // seeking around, but not all users of FileFd use always Seek() and co
1534 // so d->seekpos isn't always true and we can just use it as a hint if
1535 // we have nothing else, but not always as an authority…
ff477ee1 1536 if (d != NULL && (d->pipe == true
c4997486 1537#ifdef HAVE_BZ2
ff477ee1 1538 || d->bz2 != NULL
c4997486 1539#endif
ff477ee1 1540 ))
1abbc47c
DK
1541 return d->seekpos;
1542
a3a03f5d 1543 off_t Res;
7efb8c8e 1544#ifdef HAVE_ZLIB
ff477ee1 1545 if (d != NULL && d->gz != NULL)
032bd56f 1546 Res = gztell(d->gz);
a3a03f5d 1547 else
699b209e 1548#endif
a3a03f5d 1549 Res = lseek(iFd,0,SEEK_CUR);
7f25bdff 1550 if (Res == (off_t)-1)
ae635e3c 1551 FileFdErrno("lseek","Failed to determine the current file position");
ff477ee1
DK
1552 if (d != NULL)
1553 d->seekpos = Res;
7f25bdff
AL
1554 return Res;
1555}
1556 /*}}}*/
4260fd39 1557// FileFd::FileSize - Return the size of the file /*{{{*/
578bfd0a
AL
1558// ---------------------------------------------------------------------
1559/* */
650faab0 1560unsigned long long FileFd::FileSize()
578bfd0a
AL
1561{
1562 struct stat Buf;
ff477ee1 1563 if ((d == NULL || d->pipe == false) && fstat(iFd,&Buf) != 0)
ae635e3c 1564 return FileFdErrno("fstat","Unable to determine the file size");
699b209e
DK
1565
1566 // for compressor pipes st_size is undefined and at 'best' zero
ff477ee1 1567 if ((d != NULL && d->pipe == true) || S_ISFIFO(Buf.st_mode))
699b209e
DK
1568 {
1569 // we set it here, too, as we get the info here for free
1570 // in theory the Open-methods should take care of it already
ff477ee1
DK
1571 if (d != NULL)
1572 d->pipe = true;
699b209e 1573 if (stat(FileName.c_str(), &Buf) != 0)
ae635e3c 1574 return FileFdErrno("stat","Unable to determine the file size");
699b209e
DK
1575 }
1576
4260fd39
DK
1577 return Buf.st_size;
1578}
1579 /*}}}*/
1580// FileFd::Size - Return the size of the content in the file /*{{{*/
1581// ---------------------------------------------------------------------
1582/* */
650faab0 1583unsigned long long FileFd::Size()
4260fd39 1584{
650faab0 1585 unsigned long long size = FileSize();
44dc669e 1586
699b209e
DK
1587 // for compressor pipes st_size is undefined and at 'best' zero,
1588 // so we 'read' the content and 'seek' back - see there
ff477ee1 1589 if (d != NULL && (d->pipe == true
c4997486 1590#ifdef HAVE_BZ2
ff477ee1 1591 || (d->bz2 && size > 0)
c4997486 1592#endif
ff477ee1 1593 ))
699b209e 1594 {
1abbc47c 1595 unsigned long long const oldSeek = Tell();
699b209e
DK
1596 char ignore[1000];
1597 unsigned long long read = 0;
1598 do {
1599 Read(ignore, sizeof(ignore), &read);
699b209e 1600 } while(read != 0);
1abbc47c
DK
1601 size = Tell();
1602 Seek(oldSeek);
699b209e 1603 }
7efb8c8e 1604#ifdef HAVE_ZLIB
44dc669e 1605 // only check gzsize if we are actually a gzip file, just checking for
032bd56f 1606 // "gz" is not sufficient as uncompressed files could be opened with
44dc669e 1607 // gzopen in "direct" mode as well
ff477ee1 1608 else if (d != NULL && d->gz && !gzdirect(d->gz) && size > 0)
9c182afa 1609 {
65c72a4b 1610 off_t const oldPos = lseek(iFd,0,SEEK_CUR);
9c182afa
MP
1611 /* unfortunately zlib.h doesn't provide a gzsize(), so we have to do
1612 * this ourselves; the original (uncompressed) file size is the last 32
1613 * bits of the file */
650faab0 1614 // FIXME: Size for gz-files is limited by 32bit… no largefile support
9c182afa 1615 if (lseek(iFd, -4, SEEK_END) < 0)
ae635e3c 1616 return FileFdErrno("lseek","Unable to seek to end of gzipped file");
f330c0f3 1617 size = 0L;
9c182afa 1618 if (read(iFd, &size, 4) != 4)
ae635e3c 1619 return FileFdErrno("read","Unable to read original size of gzipped file");
f330c0f3
DK
1620
1621#ifdef WORDS_BIGENDIAN
2cae0ccb
DK
1622 uint32_t tmp_size = size;
1623 uint8_t const * const p = (uint8_t const * const) &tmp_size;
1624 tmp_size = (p[3] << 24) | (p[2] << 16) | (p[1] << 8) | p[0];
1625 size = tmp_size;
f330c0f3 1626#endif
9c182afa 1627
65c72a4b 1628 if (lseek(iFd, oldPos, SEEK_SET) < 0)
ae635e3c 1629 return FileFdErrno("lseek","Unable to seek in gzipped file");
65c72a4b 1630
9c182afa
MP
1631 return size;
1632 }
699b209e 1633#endif
9c182afa 1634
44dc669e 1635 return size;
578bfd0a
AL
1636}
1637 /*}}}*/
76a763e1
DK
1638// FileFd::ModificationTime - Return the time of last touch /*{{{*/
1639// ---------------------------------------------------------------------
1640/* */
1641time_t FileFd::ModificationTime()
1642{
1643 struct stat Buf;
ff477ee1 1644 if ((d == NULL || d->pipe == false) && fstat(iFd,&Buf) != 0)
76a763e1 1645 {
ae635e3c 1646 FileFdErrno("fstat","Unable to determine the modification time of file %s", FileName.c_str());
76a763e1
DK
1647 return 0;
1648 }
699b209e
DK
1649
1650 // for compressor pipes st_size is undefined and at 'best' zero
ff477ee1 1651 if ((d != NULL && d->pipe == true) || S_ISFIFO(Buf.st_mode))
699b209e
DK
1652 {
1653 // we set it here, too, as we get the info here for free
1654 // in theory the Open-methods should take care of it already
ff477ee1
DK
1655 if (d != NULL)
1656 d->pipe = true;
699b209e
DK
1657 if (stat(FileName.c_str(), &Buf) != 0)
1658 {
ae635e3c 1659 FileFdErrno("fstat","Unable to determine the modification time of file %s", FileName.c_str());
699b209e
DK
1660 return 0;
1661 }
1662 }
1663
76a763e1
DK
1664 return Buf.st_mtime;
1665}
1666 /*}}}*/
8e06abb2 1667// FileFd::Close - Close the file if the close flag is set /*{{{*/
578bfd0a
AL
1668// ---------------------------------------------------------------------
1669/* */
8e06abb2 1670bool FileFd::Close()
578bfd0a 1671{
032bd56f
DK
1672 if (iFd == -1)
1673 return true;
1674
578bfd0a
AL
1675 bool Res = true;
1676 if ((Flags & AutoClose) == AutoClose)
d13c2d3f 1677 {
500400fe
DK
1678 if ((Flags & Compressed) != Compressed && iFd > 0 && close(iFd) != 0)
1679 Res &= _error->Errno("close",_("Problem closing the file %s"), FileName.c_str());
1680
1681 if (d != NULL)
1682 {
1683 Res &= d->CloseDown(FileName);
1684 delete d;
1685 d = NULL;
1686 }
d13c2d3f 1687 }
3010fb0e 1688
d3aac32e 1689 if ((Flags & Replace) == Replace) {
3010fb0e 1690 if (rename(TemporaryFileName.c_str(), FileName.c_str()) != 0)
62d073d9
DK
1691 Res &= _error->Errno("rename",_("Problem renaming the file %s to %s"), TemporaryFileName.c_str(), FileName.c_str());
1692
fd3b761e 1693 FileName = TemporaryFileName; // for the unlink() below.
257e8d66 1694 TemporaryFileName.clear();
3010fb0e 1695 }
62d073d9
DK
1696
1697 iFd = -1;
1698
578bfd0a
AL
1699 if ((Flags & Fail) == Fail && (Flags & DelOnFail) == DelOnFail &&
1700 FileName.empty() == false)
1701 if (unlink(FileName.c_str()) != 0)
62d073d9 1702 Res &= _error->WarningE("unlnk",_("Problem unlinking the file %s"), FileName.c_str());
3010fb0e 1703
fbb89d94
DK
1704 if (Res == false)
1705 Flags |= Fail;
578bfd0a
AL
1706 return Res;
1707}
1708 /*}}}*/
b2e465d6
AL
1709// FileFd::Sync - Sync the file /*{{{*/
1710// ---------------------------------------------------------------------
1711/* */
1712bool FileFd::Sync()
1713{
b2e465d6 1714 if (fsync(iFd) != 0)
ae635e3c
DK
1715 return FileFdErrno("sync",_("Problem syncing the file"));
1716 return true;
1717}
1718 /*}}}*/
1719// FileFd::FileFdErrno - set Fail and call _error->Errno *{{{*/
1720bool FileFd::FileFdErrno(const char *Function, const char *Description,...)
1721{
1722 Flags |= Fail;
1723 va_list args;
1724 size_t msgSize = 400;
1725 int const errsv = errno;
1726 while (true)
fbb89d94 1727 {
ae635e3c
DK
1728 va_start(args,Description);
1729 if (_error->InsertErrno(GlobalError::ERROR, Function, Description, args, errsv, msgSize) == false)
1730 break;
1731 va_end(args);
fbb89d94 1732 }
ae635e3c
DK
1733 return false;
1734}
1735 /*}}}*/
1736// FileFd::FileFdError - set Fail and call _error->Error *{{{*/
1737bool FileFd::FileFdError(const char *Description,...) {
1738 Flags |= Fail;
1739 va_list args;
1740 size_t msgSize = 400;
1741 while (true)
1742 {
1743 va_start(args,Description);
1744 if (_error->Insert(GlobalError::ERROR, Description, args, msgSize) == false)
1745 break;
1746 va_end(args);
1747 }
1748 return false;
b2e465d6
AL
1749}
1750 /*}}}*/
699b209e
DK
1751
1752gzFile FileFd::gzFd() { return (gzFile) d->gz; }