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