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