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