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