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