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