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