]> git.saurik.com Git - apt.git/blame - apt-pkg/contrib/fileutl.cc
merged from lp:~donkult/apt/experimental
[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);
6fd947bd 1157 iFd = 0;
561f860a
DK
1158 if (TemporaryFileName.empty() == false)
1159 iFd = open(TemporaryFileName.c_str(), O_RDONLY);
1160 else if (FileName.empty() == false)
1161 iFd = open(FileName.c_str(), O_RDONLY);
1162 else
6fd947bd
DK
1163 {
1164 if (d->compressed_fd > 0)
1165 if (lseek(d->compressed_fd, 0, SEEK_SET) != 0)
1166 iFd = d->compressed_fd;
1167 if (iFd <= 0)
1168 return _error->Error("Reopen is not implemented for pipes opened with FileFd::OpenDescriptor()!");
1169 }
561f860a
DK
1170
1171 if (OpenInternDescriptor(d->openmode, d->compressor) == false)
1172 return _error->Error("Seek on file %s because it couldn't be reopened", FileName.c_str());
1173
1174 if (To != 0)
1175 return Skip(To);
1176 return true;
699b209e 1177 }
a3a03f5d 1178 int res;
aee1aac6 1179#if APT_USE_ZLIB
032bd56f
DK
1180 if (d->gz)
1181 res = gzseek(d->gz,To,SEEK_SET);
a3a03f5d 1182 else
699b209e 1183#endif
a3a03f5d 1184 res = lseek(iFd,To,SEEK_SET);
1185 if (res != (signed)To)
578bfd0a
AL
1186 {
1187 Flags |= Fail;
650faab0 1188 return _error->Error("Unable to seek to %llu", To);
578bfd0a
AL
1189 }
1190
727f18af
AL
1191 return true;
1192}
1193 /*}}}*/
1194// FileFd::Skip - Seek in the file /*{{{*/
1195// ---------------------------------------------------------------------
1196/* */
650faab0 1197bool FileFd::Skip(unsigned long long Over)
727f18af 1198{
a3a03f5d 1199 int res;
aee1aac6 1200#if APT_USE_ZLIB
032bd56f
DK
1201 if (d->gz != NULL)
1202 res = gzseek(d->gz,Over,SEEK_CUR);
a3a03f5d 1203 else
699b209e 1204#endif
a3a03f5d 1205 res = lseek(iFd,Over,SEEK_CUR);
1206 if (res < 0)
727f18af
AL
1207 {
1208 Flags |= Fail;
650faab0 1209 return _error->Error("Unable to seek ahead %llu",Over);
727f18af
AL
1210 }
1211
6d5dd02a
AL
1212 return true;
1213}
1214 /*}}}*/
1215// FileFd::Truncate - Truncate the file /*{{{*/
1216// ---------------------------------------------------------------------
1217/* */
650faab0 1218bool FileFd::Truncate(unsigned long long To)
6d5dd02a 1219{
032bd56f 1220 if (d->gz != NULL)
a3a03f5d 1221 {
1222 Flags |= Fail;
1223 return _error->Error("Truncating gzipped files is not implemented (%s)", FileName.c_str());
1224 }
6d5dd02a
AL
1225 if (ftruncate(iFd,To) != 0)
1226 {
1227 Flags |= Fail;
650faab0 1228 return _error->Error("Unable to truncate to %llu",To);
6d5dd02a
AL
1229 }
1230
578bfd0a
AL
1231 return true;
1232}
1233 /*}}}*/
7f25bdff
AL
1234// FileFd::Tell - Current seek position /*{{{*/
1235// ---------------------------------------------------------------------
1236/* */
650faab0 1237unsigned long long FileFd::Tell()
7f25bdff 1238{
a3a03f5d 1239 off_t Res;
aee1aac6 1240#if APT_USE_ZLIB
032bd56f
DK
1241 if (d->gz != NULL)
1242 Res = gztell(d->gz);
a3a03f5d 1243 else
699b209e 1244#endif
a3a03f5d 1245 Res = lseek(iFd,0,SEEK_CUR);
7f25bdff
AL
1246 if (Res == (off_t)-1)
1247 _error->Errno("lseek","Failed to determine the current file position");
1248 return Res;
1249}
1250 /*}}}*/
4260fd39 1251// FileFd::FileSize - Return the size of the file /*{{{*/
578bfd0a
AL
1252// ---------------------------------------------------------------------
1253/* */
650faab0 1254unsigned long long FileFd::FileSize()
578bfd0a
AL
1255{
1256 struct stat Buf;
699b209e 1257 if (d->pipe == false && fstat(iFd,&Buf) != 0)
44dc669e 1258 return _error->Errno("fstat","Unable to determine the file size");
699b209e
DK
1259
1260 // for compressor pipes st_size is undefined and at 'best' zero
1261 if (d->pipe == true || S_ISFIFO(Buf.st_mode))
1262 {
1263 // we set it here, too, as we get the info here for free
1264 // in theory the Open-methods should take care of it already
1265 d->pipe = true;
1266 if (stat(FileName.c_str(), &Buf) != 0)
1267 return _error->Errno("stat","Unable to determine the file size");
1268 }
1269
4260fd39
DK
1270 return Buf.st_size;
1271}
1272 /*}}}*/
1273// FileFd::Size - Return the size of the content in the file /*{{{*/
1274// ---------------------------------------------------------------------
1275/* */
650faab0 1276unsigned long long FileFd::Size()
4260fd39 1277{
650faab0 1278 unsigned long long size = FileSize();
44dc669e 1279
699b209e
DK
1280 // for compressor pipes st_size is undefined and at 'best' zero,
1281 // so we 'read' the content and 'seek' back - see there
1282 if (d->pipe == true)
1283 {
1284 // FIXME: If we have read first and then FileSize() the report is wrong
1285 size = 0;
1286 char ignore[1000];
1287 unsigned long long read = 0;
1288 do {
1289 Read(ignore, sizeof(ignore), &read);
1290 size += read;
1291 } while(read != 0);
1292 Seek(0);
1293 }
aee1aac6 1294#if APT_USE_ZLIB
44dc669e 1295 // only check gzsize if we are actually a gzip file, just checking for
032bd56f 1296 // "gz" is not sufficient as uncompressed files could be opened with
44dc669e 1297 // gzopen in "direct" mode as well
699b209e 1298 else if (d->gz && !gzdirect(d->gz) && size > 0)
9c182afa
MP
1299 {
1300 /* unfortunately zlib.h doesn't provide a gzsize(), so we have to do
1301 * this ourselves; the original (uncompressed) file size is the last 32
1302 * bits of the file */
650faab0 1303 // FIXME: Size for gz-files is limited by 32bit… no largefile support
4260fd39 1304 off_t orig_pos = lseek(iFd, 0, SEEK_CUR);
9c182afa
MP
1305 if (lseek(iFd, -4, SEEK_END) < 0)
1306 return _error->Errno("lseek","Unable to seek to end of gzipped file");
f330c0f3 1307 size = 0L;
9c182afa
MP
1308 if (read(iFd, &size, 4) != 4)
1309 return _error->Errno("read","Unable to read original size of gzipped file");
f330c0f3
DK
1310
1311#ifdef WORDS_BIGENDIAN
2cae0ccb
DK
1312 uint32_t tmp_size = size;
1313 uint8_t const * const p = (uint8_t const * const) &tmp_size;
1314 tmp_size = (p[3] << 24) | (p[2] << 16) | (p[1] << 8) | p[0];
1315 size = tmp_size;
f330c0f3 1316#endif
9c182afa
MP
1317
1318 if (lseek(iFd, orig_pos, SEEK_SET) < 0)
1319 return _error->Errno("lseek","Unable to seek in gzipped file");
1320 return size;
1321 }
699b209e 1322#endif
9c182afa 1323
44dc669e 1324 return size;
578bfd0a
AL
1325}
1326 /*}}}*/
76a763e1
DK
1327// FileFd::ModificationTime - Return the time of last touch /*{{{*/
1328// ---------------------------------------------------------------------
1329/* */
1330time_t FileFd::ModificationTime()
1331{
1332 struct stat Buf;
699b209e 1333 if (d->pipe == false && fstat(iFd,&Buf) != 0)
76a763e1
DK
1334 {
1335 _error->Errno("fstat","Unable to determine the modification time of file %s", FileName.c_str());
1336 return 0;
1337 }
699b209e
DK
1338
1339 // for compressor pipes st_size is undefined and at 'best' zero
1340 if (d->pipe == true || S_ISFIFO(Buf.st_mode))
1341 {
1342 // we set it here, too, as we get the info here for free
1343 // in theory the Open-methods should take care of it already
1344 d->pipe = true;
1345 if (stat(FileName.c_str(), &Buf) != 0)
1346 {
1347 _error->Errno("fstat","Unable to determine the modification time of file %s", FileName.c_str());
1348 return 0;
1349 }
1350 }
1351
76a763e1
DK
1352 return Buf.st_mtime;
1353}
1354 /*}}}*/
8e06abb2 1355// FileFd::Close - Close the file if the close flag is set /*{{{*/
578bfd0a
AL
1356// ---------------------------------------------------------------------
1357/* */
8e06abb2 1358bool FileFd::Close()
578bfd0a 1359{
032bd56f
DK
1360 if (iFd == -1)
1361 return true;
1362
578bfd0a
AL
1363 bool Res = true;
1364 if ((Flags & AutoClose) == AutoClose)
d13c2d3f 1365 {
aee1aac6 1366#if APT_USE_ZLIB
032bd56f
DK
1367 if (d != NULL && d->gz != NULL) {
1368 int const e = gzclose(d->gz);
d13c2d3f 1369 // gzdopen() on empty files always fails with "buffer error" here, ignore that
1370 if (e != 0 && e != Z_BUF_ERROR)
3184b4cf 1371 Res &= _error->Errno("close",_("Problem closing the gzip file %s"), FileName.c_str());
d13c2d3f 1372 } else
699b209e 1373#endif
d13c2d3f 1374 if (iFd > 0 && close(iFd) != 0)
3184b4cf 1375 Res &= _error->Errno("close",_("Problem closing the file %s"), FileName.c_str());
d13c2d3f 1376 }
3010fb0e 1377
62d073d9 1378 if ((Flags & Replace) == Replace && iFd >= 0) {
3010fb0e 1379 if (rename(TemporaryFileName.c_str(), FileName.c_str()) != 0)
62d073d9
DK
1380 Res &= _error->Errno("rename",_("Problem renaming the file %s to %s"), TemporaryFileName.c_str(), FileName.c_str());
1381
fd3b761e 1382 FileName = TemporaryFileName; // for the unlink() below.
257e8d66 1383 TemporaryFileName.clear();
3010fb0e 1384 }
62d073d9
DK
1385
1386 iFd = -1;
1387
578bfd0a
AL
1388 if ((Flags & Fail) == Fail && (Flags & DelOnFail) == DelOnFail &&
1389 FileName.empty() == false)
1390 if (unlink(FileName.c_str()) != 0)
62d073d9 1391 Res &= _error->WarningE("unlnk",_("Problem unlinking the file %s"), FileName.c_str());
3010fb0e 1392
032bd56f
DK
1393 if (d != NULL)
1394 {
561f860a 1395 if (d->compressor_pid > 0)
52b47296 1396 ExecWait(d->compressor_pid, "FileFdCompressor", true);
032bd56f
DK
1397 delete d;
1398 d = NULL;
1399 }
3010fb0e 1400
578bfd0a
AL
1401 return Res;
1402}
1403 /*}}}*/
b2e465d6
AL
1404// FileFd::Sync - Sync the file /*{{{*/
1405// ---------------------------------------------------------------------
1406/* */
1407bool FileFd::Sync()
1408{
1409#ifdef _POSIX_SYNCHRONIZED_IO
1410 if (fsync(iFd) != 0)
1411 return _error->Errno("sync",_("Problem syncing the file"));
1412#endif
1413 return true;
1414}
1415 /*}}}*/
699b209e
DK
1416
1417gzFile FileFd::gzFd() { return (gzFile) d->gz; }