]> git.saurik.com Git - apt.git/blob - apt-pkg/contrib/fileutl.cc
merged from debian-experimental2
[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 // do not show ignoration warnings for directories
391 if (
392 #ifdef _DIRENT_HAVE_D_TYPE
393 Ent->d_type == DT_DIR ||
394 #endif
395 DirectoryExists(File.c_str()) == true)
396 continue;
397 if (SilentIgnore.Match(Ent->d_name) == false)
398 _error->Notice(_("Ignoring '%s' in directory '%s' as it is not a regular file"), Ent->d_name, Dir.c_str());
399 continue;
400 }
401 }
402
403 // check for accepted extension:
404 // no extension given -> periods are bad as hell!
405 // extensions given -> "" extension allows no extension
406 if (Ext.empty() == false)
407 {
408 string d_ext = flExtension(Ent->d_name);
409 if (d_ext == Ent->d_name) // no extension
410 {
411 if (std::find(Ext.begin(), Ext.end(), "") == Ext.end())
412 {
413 if (Debug == true)
414 std::clog << "Bad file: " << Ent->d_name << " → no extension" << std::endl;
415 if (SilentIgnore.Match(Ent->d_name) == false)
416 _error->Notice(_("Ignoring file '%s' in directory '%s' as it has no filename extension"), Ent->d_name, Dir.c_str());
417 continue;
418 }
419 }
420 else if (std::find(Ext.begin(), Ext.end(), d_ext) == Ext.end())
421 {
422 if (Debug == true)
423 std::clog << "Bad file: " << Ent->d_name << " → bad extension »" << flExtension(Ent->d_name) << "«" << std::endl;
424 if (SilentIgnore.Match(Ent->d_name) == false)
425 _error->Notice(_("Ignoring file '%s' in directory '%s' as it has an invalid filename extension"), Ent->d_name, Dir.c_str());
426 continue;
427 }
428 }
429
430 // Skip bad filenames ala run-parts
431 const char *C = Ent->d_name;
432 for (; *C != 0; ++C)
433 if (isalpha(*C) == 0 && isdigit(*C) == 0
434 && *C != '_' && *C != '-') {
435 // no required extension -> dot is a bad character
436 if (*C == '.' && Ext.empty() == false)
437 continue;
438 break;
439 }
440
441 // we don't reach the end of the name -> bad character included
442 if (*C != 0)
443 {
444 if (Debug == true)
445 std::clog << "Bad file: " << Ent->d_name << " → bad character »"
446 << *C << "« in filename (period allowed: " << (Ext.empty() ? "no" : "yes") << ")" << std::endl;
447 continue;
448 }
449
450 // skip filenames which end with a period. These are never valid
451 if (*(C - 1) == '.')
452 {
453 if (Debug == true)
454 std::clog << "Bad file: " << Ent->d_name << " → Period as last character" << std::endl;
455 continue;
456 }
457
458 if (Debug == true)
459 std::clog << "Accept file: " << Ent->d_name << " in " << Dir << std::endl;
460 List.push_back(File);
461 }
462 closedir(D);
463
464 if (SortList == true)
465 std::sort(List.begin(),List.end());
466 return List;
467 }
468 std::vector<string> GetListOfFilesInDir(string const &Dir, bool SortList)
469 {
470 bool const Debug = _config->FindB("Debug::GetListOfFilesInDir", false);
471 if (Debug == true)
472 std::clog << "Accept in " << Dir << " all regular files" << std::endl;
473
474 std::vector<string> List;
475
476 if (DirectoryExists(Dir.c_str()) == false)
477 {
478 _error->Error(_("List of files can't be created as '%s' is not a directory"), Dir.c_str());
479 return List;
480 }
481
482 DIR *D = opendir(Dir.c_str());
483 if (D == 0)
484 {
485 _error->Errno("opendir",_("Unable to read %s"),Dir.c_str());
486 return List;
487 }
488
489 for (struct dirent *Ent = readdir(D); Ent != 0; Ent = readdir(D))
490 {
491 // skip "hidden" files
492 if (Ent->d_name[0] == '.')
493 continue;
494
495 // Make sure it is a file and not something else
496 string const File = flCombine(Dir,Ent->d_name);
497 #ifdef _DIRENT_HAVE_D_TYPE
498 if (Ent->d_type != DT_REG)
499 #endif
500 {
501 if (RealFileExists(File.c_str()) == false)
502 {
503 if (Debug == true)
504 std::clog << "Bad file: " << Ent->d_name << " → it is not a real file" << std::endl;
505 continue;
506 }
507 }
508
509 // Skip bad filenames ala run-parts
510 const char *C = Ent->d_name;
511 for (; *C != 0; ++C)
512 if (isalpha(*C) == 0 && isdigit(*C) == 0
513 && *C != '_' && *C != '-' && *C != '.')
514 break;
515
516 // we don't reach the end of the name -> bad character included
517 if (*C != 0)
518 {
519 if (Debug == true)
520 std::clog << "Bad file: " << Ent->d_name << " → bad character »" << *C << "« in filename" << std::endl;
521 continue;
522 }
523
524 // skip filenames which end with a period. These are never valid
525 if (*(C - 1) == '.')
526 {
527 if (Debug == true)
528 std::clog << "Bad file: " << Ent->d_name << " → Period as last character" << std::endl;
529 continue;
530 }
531
532 if (Debug == true)
533 std::clog << "Accept file: " << Ent->d_name << " in " << Dir << std::endl;
534 List.push_back(File);
535 }
536 closedir(D);
537
538 if (SortList == true)
539 std::sort(List.begin(),List.end());
540 return List;
541 }
542 /*}}}*/
543 // SafeGetCWD - This is a safer getcwd that returns a dynamic string /*{{{*/
544 // ---------------------------------------------------------------------
545 /* We return / on failure. */
546 string SafeGetCWD()
547 {
548 // Stash the current dir.
549 char S[300];
550 S[0] = 0;
551 if (getcwd(S,sizeof(S)-2) == 0)
552 return "/";
553 unsigned int Len = strlen(S);
554 S[Len] = '/';
555 S[Len+1] = 0;
556 return S;
557 }
558 /*}}}*/
559 // GetModificationTime - Get the mtime of the given file or -1 on error /*{{{*/
560 // ---------------------------------------------------------------------
561 /* We return / on failure. */
562 time_t GetModificationTime(string const &Path)
563 {
564 struct stat St;
565 if (stat(Path.c_str(), &St) < 0)
566 return -1;
567 return St.st_mtime;
568 }
569 /*}}}*/
570 // flNotDir - Strip the directory from the filename /*{{{*/
571 // ---------------------------------------------------------------------
572 /* */
573 string flNotDir(string File)
574 {
575 string::size_type Res = File.rfind('/');
576 if (Res == string::npos)
577 return File;
578 Res++;
579 return string(File,Res,Res - File.length());
580 }
581 /*}}}*/
582 // flNotFile - Strip the file from the directory name /*{{{*/
583 // ---------------------------------------------------------------------
584 /* Result ends in a / */
585 string flNotFile(string File)
586 {
587 string::size_type Res = File.rfind('/');
588 if (Res == string::npos)
589 return "./";
590 Res++;
591 return string(File,0,Res);
592 }
593 /*}}}*/
594 // flExtension - Return the extension for the file /*{{{*/
595 // ---------------------------------------------------------------------
596 /* */
597 string flExtension(string File)
598 {
599 string::size_type Res = File.rfind('.');
600 if (Res == string::npos)
601 return File;
602 Res++;
603 return string(File,Res,Res - File.length());
604 }
605 /*}}}*/
606 // flNoLink - If file is a symlink then deref it /*{{{*/
607 // ---------------------------------------------------------------------
608 /* If the name is not a link then the returned path is the input. */
609 string flNoLink(string File)
610 {
611 struct stat St;
612 if (lstat(File.c_str(),&St) != 0 || S_ISLNK(St.st_mode) == 0)
613 return File;
614 if (stat(File.c_str(),&St) != 0)
615 return File;
616
617 /* Loop resolving the link. There is no need to limit the number of
618 loops because the stat call above ensures that the symlink is not
619 circular */
620 char Buffer[1024];
621 string NFile = File;
622 while (1)
623 {
624 // Read the link
625 int Res;
626 if ((Res = readlink(NFile.c_str(),Buffer,sizeof(Buffer))) <= 0 ||
627 (unsigned)Res >= sizeof(Buffer))
628 return File;
629
630 // Append or replace the previous path
631 Buffer[Res] = 0;
632 if (Buffer[0] == '/')
633 NFile = Buffer;
634 else
635 NFile = flNotFile(NFile) + Buffer;
636
637 // See if we are done
638 if (lstat(NFile.c_str(),&St) != 0)
639 return File;
640 if (S_ISLNK(St.st_mode) == 0)
641 return NFile;
642 }
643 }
644 /*}}}*/
645 // flCombine - Combine a file and a directory /*{{{*/
646 // ---------------------------------------------------------------------
647 /* If the file is an absolute path then it is just returned, otherwise
648 the directory is pre-pended to it. */
649 string flCombine(string Dir,string File)
650 {
651 if (File.empty() == true)
652 return string();
653
654 if (File[0] == '/' || Dir.empty() == true)
655 return File;
656 if (File.length() >= 2 && File[0] == '.' && File[1] == '/')
657 return File;
658 if (Dir[Dir.length()-1] == '/')
659 return Dir + File;
660 return Dir + '/' + File;
661 }
662 /*}}}*/
663 // SetCloseExec - Set the close on exec flag /*{{{*/
664 // ---------------------------------------------------------------------
665 /* */
666 void SetCloseExec(int Fd,bool Close)
667 {
668 if (fcntl(Fd,F_SETFD,(Close == false)?0:FD_CLOEXEC) != 0)
669 {
670 cerr << "FATAL -> Could not set close on exec " << strerror(errno) << endl;
671 exit(100);
672 }
673 }
674 /*}}}*/
675 // SetNonBlock - Set the nonblocking flag /*{{{*/
676 // ---------------------------------------------------------------------
677 /* */
678 void SetNonBlock(int Fd,bool Block)
679 {
680 int Flags = fcntl(Fd,F_GETFL) & (~O_NONBLOCK);
681 if (fcntl(Fd,F_SETFL,Flags | ((Block == false)?0:O_NONBLOCK)) != 0)
682 {
683 cerr << "FATAL -> Could not set non-blocking flag " << strerror(errno) << endl;
684 exit(100);
685 }
686 }
687 /*}}}*/
688 // WaitFd - Wait for a FD to become readable /*{{{*/
689 // ---------------------------------------------------------------------
690 /* This waits for a FD to become readable using select. It is useful for
691 applications making use of non-blocking sockets. The timeout is
692 in seconds. */
693 bool WaitFd(int Fd,bool write,unsigned long timeout)
694 {
695 fd_set Set;
696 struct timeval tv;
697 FD_ZERO(&Set);
698 FD_SET(Fd,&Set);
699 tv.tv_sec = timeout;
700 tv.tv_usec = 0;
701 if (write == true)
702 {
703 int Res;
704 do
705 {
706 Res = select(Fd+1,0,&Set,0,(timeout != 0?&tv:0));
707 }
708 while (Res < 0 && errno == EINTR);
709
710 if (Res <= 0)
711 return false;
712 }
713 else
714 {
715 int Res;
716 do
717 {
718 Res = select(Fd+1,&Set,0,0,(timeout != 0?&tv:0));
719 }
720 while (Res < 0 && errno == EINTR);
721
722 if (Res <= 0)
723 return false;
724 }
725
726 return true;
727 }
728 /*}}}*/
729 // ExecFork - Magical fork that sanitizes the context before execing /*{{{*/
730 // ---------------------------------------------------------------------
731 /* This is used if you want to cleanse the environment for the forked
732 child, it fixes up the important signals and nukes all of the fds,
733 otherwise acts like normal fork. */
734 pid_t ExecFork()
735 {
736 // Fork off the process
737 pid_t Process = fork();
738 if (Process < 0)
739 {
740 cerr << "FATAL -> Failed to fork." << endl;
741 exit(100);
742 }
743
744 // Spawn the subprocess
745 if (Process == 0)
746 {
747 // Setup the signals
748 signal(SIGPIPE,SIG_DFL);
749 signal(SIGQUIT,SIG_DFL);
750 signal(SIGINT,SIG_DFL);
751 signal(SIGWINCH,SIG_DFL);
752 signal(SIGCONT,SIG_DFL);
753 signal(SIGTSTP,SIG_DFL);
754
755 set<int> KeepFDs;
756 Configuration::Item const *Opts = _config->Tree("APT::Keep-Fds");
757 if (Opts != 0 && Opts->Child != 0)
758 {
759 Opts = Opts->Child;
760 for (; Opts != 0; Opts = Opts->Next)
761 {
762 if (Opts->Value.empty() == true)
763 continue;
764 int fd = atoi(Opts->Value.c_str());
765 KeepFDs.insert(fd);
766 }
767 }
768
769 // Close all of our FDs - just in case
770 for (int K = 3; K != 40; K++)
771 {
772 if(KeepFDs.find(K) == KeepFDs.end())
773 fcntl(K,F_SETFD,FD_CLOEXEC);
774 }
775 }
776
777 return Process;
778 }
779 /*}}}*/
780 // ExecWait - Fancy waitpid /*{{{*/
781 // ---------------------------------------------------------------------
782 /* Waits for the given sub process. If Reap is set then no errors are
783 generated. Otherwise a failed subprocess will generate a proper descriptive
784 message */
785 bool ExecWait(pid_t Pid,const char *Name,bool Reap)
786 {
787 if (Pid <= 1)
788 return true;
789
790 // Wait and collect the error code
791 int Status;
792 while (waitpid(Pid,&Status,0) != Pid)
793 {
794 if (errno == EINTR)
795 continue;
796
797 if (Reap == true)
798 return false;
799
800 return _error->Error(_("Waited for %s but it wasn't there"),Name);
801 }
802
803
804 // Check for an error code.
805 if (WIFEXITED(Status) == 0 || WEXITSTATUS(Status) != 0)
806 {
807 if (Reap == true)
808 return false;
809 if (WIFSIGNALED(Status) != 0)
810 {
811 if( WTERMSIG(Status) == SIGSEGV)
812 return _error->Error(_("Sub-process %s received a segmentation fault."),Name);
813 else
814 return _error->Error(_("Sub-process %s received signal %u."),Name, WTERMSIG(Status));
815 }
816
817 if (WIFEXITED(Status) != 0)
818 return _error->Error(_("Sub-process %s returned an error code (%u)"),Name,WEXITSTATUS(Status));
819
820 return _error->Error(_("Sub-process %s exited unexpectedly"),Name);
821 }
822
823 return true;
824 }
825 /*}}}*/
826
827 // FileFd::Open - Open a file /*{{{*/
828 // ---------------------------------------------------------------------
829 /* The most commonly used open mode combinations are given with Mode */
830 bool FileFd::Open(string FileName,unsigned int const Mode,CompressMode Compress, unsigned long const Perms)
831 {
832 if (Mode == ReadOnlyGzip)
833 return Open(FileName, ReadOnly, Gzip, Perms);
834
835 if (Compress == Auto && (Mode & WriteOnly) == WriteOnly)
836 return _error->Error("Autodetection on %s only works in ReadOnly openmode!", FileName.c_str());
837
838 // FIXME: Denote inbuilt compressors somehow - as we don't need to have the binaries for them
839 std::vector<APT::Configuration::Compressor> const compressors = APT::Configuration::getCompressors();
840 std::vector<APT::Configuration::Compressor>::const_iterator compressor = compressors.begin();
841 if (Compress == Auto)
842 {
843 for (; compressor != compressors.end(); ++compressor)
844 {
845 std::string file = std::string(FileName).append(compressor->Extension);
846 if (FileExists(file) == false)
847 continue;
848 FileName = file;
849 break;
850 }
851 }
852 else if (Compress == Extension)
853 {
854 std::string::size_type const found = FileName.find_last_of('.');
855 std::string ext;
856 if (found != std::string::npos)
857 {
858 ext = FileName.substr(found);
859 if (ext == ".new" || ext == ".bak")
860 {
861 std::string::size_type const found2 = FileName.find_last_of('.', found - 1);
862 if (found2 != std::string::npos)
863 ext = FileName.substr(found2, found - found2);
864 else
865 ext.clear();
866 }
867 }
868 for (; compressor != compressors.end(); ++compressor)
869 if (ext == compressor->Extension)
870 break;
871 // no matching extension - assume uncompressed (imagine files like 'example.org_Packages')
872 if (compressor == compressors.end())
873 for (compressor = compressors.begin(); compressor != compressors.end(); ++compressor)
874 if (compressor->Name == ".")
875 break;
876 }
877 else
878 {
879 std::string name;
880 switch (Compress)
881 {
882 case None: name = "."; break;
883 case Gzip: name = "gzip"; break;
884 case Bzip2: name = "bzip2"; break;
885 case Lzma: name = "lzma"; break;
886 case Xz: name = "xz"; break;
887 case Auto:
888 case Extension:
889 // Unreachable
890 return _error->Error("Opening File %s in None, Auto or Extension should be already handled?!?", FileName.c_str());
891 }
892 for (; compressor != compressors.end(); ++compressor)
893 if (compressor->Name == name)
894 break;
895 if (compressor == compressors.end())
896 return _error->Error("Can't find a configured compressor %s for file %s", name.c_str(), FileName.c_str());
897 }
898
899 if (compressor == compressors.end())
900 return _error->Error("Can't find a match for specified compressor mode for file %s", FileName.c_str());
901 return Open(FileName, Mode, *compressor, Perms);
902 }
903 bool FileFd::Open(string FileName,unsigned int const Mode,APT::Configuration::Compressor const &compressor, unsigned long const Perms)
904 {
905 Close();
906 d = new FileFdPrivate;
907 d->openmode = Mode;
908 Flags = AutoClose;
909
910 if ((Mode & WriteOnly) != WriteOnly && (Mode & (Atomic | Create | Empty | Exclusive)) != 0)
911 return _error->Error("ReadOnly mode for %s doesn't accept additional flags!", FileName.c_str());
912 if ((Mode & ReadWrite) == 0)
913 return _error->Error("No openmode provided in FileFd::Open for %s", FileName.c_str());
914
915 if ((Mode & Atomic) == Atomic)
916 {
917 Flags |= Replace;
918 char *name = strdup((FileName + ".XXXXXX").c_str());
919 TemporaryFileName = string(mktemp(name));
920 free(name);
921 }
922 else if ((Mode & (Exclusive | Create)) == (Exclusive | Create))
923 {
924 // for atomic, this will be done by rename in Close()
925 unlink(FileName.c_str());
926 }
927 if ((Mode & Empty) == Empty)
928 {
929 struct stat Buf;
930 if (lstat(FileName.c_str(),&Buf) == 0 && S_ISLNK(Buf.st_mode))
931 unlink(FileName.c_str());
932 }
933
934 int fileflags = 0;
935 #define if_FLAGGED_SET(FLAG, MODE) if ((Mode & FLAG) == FLAG) fileflags |= MODE
936 if_FLAGGED_SET(ReadWrite, O_RDWR);
937 else if_FLAGGED_SET(ReadOnly, O_RDONLY);
938 else if_FLAGGED_SET(WriteOnly, O_WRONLY);
939
940 if_FLAGGED_SET(Create, O_CREAT);
941 if_FLAGGED_SET(Empty, O_TRUNC);
942 if_FLAGGED_SET(Exclusive, O_EXCL);
943 else if_FLAGGED_SET(Atomic, O_EXCL);
944 #undef if_FLAGGED_SET
945
946 if (TemporaryFileName.empty() == false)
947 iFd = open(TemporaryFileName.c_str(), fileflags, Perms);
948 else
949 iFd = open(FileName.c_str(), fileflags, Perms);
950
951 this->FileName = FileName;
952 if (iFd == -1 || OpenInternDescriptor(Mode, compressor) == false)
953 {
954 if (iFd != -1)
955 {
956 close (iFd);
957 iFd = -1;
958 }
959 return _error->Errno("open",_("Could not open file %s"), FileName.c_str());
960 }
961
962 SetCloseExec(iFd,true);
963 return true;
964 }
965 /*}}}*/
966 // FileFd::OpenDescriptor - Open a filedescriptor /*{{{*/
967 // ---------------------------------------------------------------------
968 /* */
969 bool FileFd::OpenDescriptor(int Fd, unsigned int const Mode, CompressMode Compress, bool AutoClose)
970 {
971 std::vector<APT::Configuration::Compressor> const compressors = APT::Configuration::getCompressors();
972 std::vector<APT::Configuration::Compressor>::const_iterator compressor = compressors.begin();
973 std::string name;
974
975 // compat with the old API
976 if (Mode == ReadOnlyGzip && Compress == None)
977 Compress = Gzip;
978
979 switch (Compress)
980 {
981 case None: name = "."; break;
982 case Gzip: name = "gzip"; break;
983 case Bzip2: name = "bzip2"; break;
984 case Lzma: name = "lzma"; break;
985 case Xz: name = "xz"; break;
986 case Auto:
987 case Extension:
988 return _error->Error("Opening Fd %d in Auto or Extension compression mode is not supported", Fd);
989 }
990 for (; compressor != compressors.end(); ++compressor)
991 if (compressor->Name == name)
992 break;
993 if (compressor == compressors.end())
994 return _error->Error("Can't find a configured compressor %s for file %s", name.c_str(), FileName.c_str());
995
996 return OpenDescriptor(Fd, Mode, *compressor, AutoClose);
997 }
998 bool FileFd::OpenDescriptor(int Fd, unsigned int const Mode, APT::Configuration::Compressor const &compressor, bool AutoClose)
999 {
1000 Close();
1001 d = new FileFdPrivate;
1002 d->openmode = Mode;
1003 Flags = (AutoClose) ? FileFd::AutoClose : 0;
1004 iFd = Fd;
1005 this->FileName = "";
1006 if (OpenInternDescriptor(Mode, compressor) == false)
1007 {
1008 if (AutoClose)
1009 close (iFd);
1010 return _error->Errno("gzdopen",_("Could not open file descriptor %d"), Fd);
1011 }
1012 return true;
1013 }
1014 bool FileFd::OpenInternDescriptor(unsigned int const Mode, APT::Configuration::Compressor const &compressor)
1015 {
1016 d->compressor = compressor;
1017 if (compressor.Name == "." || compressor.Binary.empty() == true)
1018 return true;
1019 #if APT_USE_ZLIB
1020 else if (compressor.Name == "gzip")
1021 {
1022 if ((Mode & ReadWrite) == ReadWrite)
1023 d->gz = gzdopen(iFd, "r+");
1024 else if ((Mode & WriteOnly) == WriteOnly)
1025 d->gz = gzdopen(iFd, "w");
1026 else
1027 d->gz = gzdopen (iFd, "r");
1028 if (d->gz == NULL)
1029 return false;
1030 Flags |= Compressed;
1031 return true;
1032 }
1033 #endif
1034
1035 if ((Mode & ReadWrite) == ReadWrite)
1036 return _error->Error("ReadWrite mode is not supported for file %s", FileName.c_str());
1037
1038 bool const Comp = (Mode & WriteOnly) == WriteOnly;
1039 // Handle 'decompression' of empty files
1040 if (Comp == false)
1041 {
1042 struct stat Buf;
1043 fstat(iFd, &Buf);
1044 if (Buf.st_size == 0 && S_ISFIFO(Buf.st_mode) == false)
1045 return true;
1046
1047 // We don't need the file open - instead let the compressor open it
1048 // as he properly knows better how to efficiently read from 'his' file
1049 if (FileName.empty() == false)
1050 close(iFd);
1051 }
1052
1053 // Create a data pipe
1054 int Pipe[2] = {-1,-1};
1055 if (pipe(Pipe) != 0)
1056 return _error->Errno("pipe",_("Failed to create subprocess IPC"));
1057 for (int J = 0; J != 2; J++)
1058 SetCloseExec(Pipe[J],true);
1059
1060 d->compressed_fd = iFd;
1061 d->pipe = true;
1062
1063 if (Comp == true)
1064 iFd = Pipe[1];
1065 else
1066 iFd = Pipe[0];
1067
1068 // The child..
1069 d->compressor_pid = ExecFork();
1070 if (d->compressor_pid == 0)
1071 {
1072 if (Comp == true)
1073 {
1074 dup2(d->compressed_fd,STDOUT_FILENO);
1075 dup2(Pipe[0],STDIN_FILENO);
1076 }
1077 else
1078 {
1079 if (FileName.empty() == true)
1080 dup2(d->compressed_fd,STDIN_FILENO);
1081 dup2(Pipe[1],STDOUT_FILENO);
1082 }
1083
1084 SetCloseExec(STDOUT_FILENO,false);
1085 SetCloseExec(STDIN_FILENO,false);
1086
1087 std::vector<char const*> Args;
1088 Args.push_back(compressor.Binary.c_str());
1089 std::vector<std::string> const * const addArgs =
1090 (Comp == true) ? &(compressor.CompressArgs) : &(compressor.UncompressArgs);
1091 for (std::vector<std::string>::const_iterator a = addArgs->begin();
1092 a != addArgs->end(); ++a)
1093 Args.push_back(a->c_str());
1094 if (Comp == false && FileName.empty() == false)
1095 {
1096 Args.push_back("--stdout");
1097 if (TemporaryFileName.empty() == false)
1098 Args.push_back(TemporaryFileName.c_str());
1099 else
1100 Args.push_back(FileName.c_str());
1101 }
1102 Args.push_back(NULL);
1103
1104 execvp(Args[0],(char **)&Args[0]);
1105 cerr << _("Failed to exec compressor ") << Args[0] << endl;
1106 _exit(100);
1107 }
1108 if (Comp == true)
1109 close(Pipe[0]);
1110 else
1111 close(Pipe[1]);
1112 if (Comp == true || FileName.empty() == true)
1113 close(d->compressed_fd);
1114
1115 return true;
1116 }
1117 /*}}}*/
1118 // FileFd::~File - Closes the file /*{{{*/
1119 // ---------------------------------------------------------------------
1120 /* If the proper modes are selected then we close the Fd and possibly
1121 unlink the file on error. */
1122 FileFd::~FileFd()
1123 {
1124 Close();
1125 }
1126 /*}}}*/
1127 // FileFd::Read - Read a bit of the file /*{{{*/
1128 // ---------------------------------------------------------------------
1129 /* We are carefull to handle interruption by a signal while reading
1130 gracefully. */
1131 bool FileFd::Read(void *To,unsigned long long Size,unsigned long long *Actual)
1132 {
1133 int Res;
1134 errno = 0;
1135 if (Actual != 0)
1136 *Actual = 0;
1137 *((char *)To) = '\0';
1138 do
1139 {
1140 #if APT_USE_ZLIB
1141 if (d->gz != NULL)
1142 Res = gzread(d->gz,To,Size);
1143 else
1144 #endif
1145 Res = read(iFd,To,Size);
1146
1147 if (Res < 0)
1148 {
1149 if (errno == EINTR)
1150 continue;
1151 Flags |= Fail;
1152 #if APT_USE_ZLIB
1153 if (d->gz != NULL)
1154 {
1155 int err;
1156 char const * const errmsg = gzerror(d->gz, &err);
1157 if (err != Z_ERRNO)
1158 return _error->Error("gzread: %s (%d: %s)", _("Read error"), err, errmsg);
1159 }
1160 #endif
1161 return _error->Errno("read",_("Read error"));
1162 }
1163
1164 To = (char *)To + Res;
1165 Size -= Res;
1166 d->seekpos += Res;
1167 if (Actual != 0)
1168 *Actual += Res;
1169 }
1170 while (Res > 0 && Size > 0);
1171
1172 if (Size == 0)
1173 return true;
1174
1175 // Eof handling
1176 if (Actual != 0)
1177 {
1178 Flags |= HitEof;
1179 return true;
1180 }
1181
1182 Flags |= Fail;
1183 return _error->Error(_("read, still have %llu to read but none left"), Size);
1184 }
1185 /*}}}*/
1186 // FileFd::ReadLine - Read a complete line from the file /*{{{*/
1187 // ---------------------------------------------------------------------
1188 /* Beware: This method can be quiet slow for big buffers on UNcompressed
1189 files because of the naive implementation! */
1190 char* FileFd::ReadLine(char *To, unsigned long long const Size)
1191 {
1192 *To = '\0';
1193 #if APT_USE_ZLIB
1194 if (d->gz != NULL)
1195 return gzgets(d->gz, To, Size);
1196 #endif
1197
1198 unsigned long long read = 0;
1199 while ((Size - 1) != read)
1200 {
1201 unsigned long long done = 0;
1202 if (Read(To + read, 1, &done) == false)
1203 return NULL;
1204 if (done == 0)
1205 break;
1206 if (To[read++] == '\n')
1207 break;
1208 }
1209 if (read == 0)
1210 return NULL;
1211 To[read] = '\0';
1212 return To;
1213 }
1214 /*}}}*/
1215 // FileFd::Write - Write to the file /*{{{*/
1216 // ---------------------------------------------------------------------
1217 /* */
1218 bool FileFd::Write(const void *From,unsigned long long Size)
1219 {
1220 int Res;
1221 errno = 0;
1222 do
1223 {
1224 #if APT_USE_ZLIB
1225 if (d->gz != NULL)
1226 Res = gzwrite(d->gz,From,Size);
1227 else
1228 #endif
1229 Res = write(iFd,From,Size);
1230 if (Res < 0 && errno == EINTR)
1231 continue;
1232 if (Res < 0)
1233 {
1234 Flags |= Fail;
1235 return _error->Errno("write",_("Write error"));
1236 }
1237
1238 From = (char *)From + Res;
1239 Size -= Res;
1240 d->seekpos += Res;
1241 }
1242 while (Res > 0 && Size > 0);
1243
1244 if (Size == 0)
1245 return true;
1246
1247 Flags |= Fail;
1248 return _error->Error(_("write, still have %llu to write but couldn't"), Size);
1249 }
1250 /*}}}*/
1251 // FileFd::Seek - Seek in the file /*{{{*/
1252 // ---------------------------------------------------------------------
1253 /* */
1254 bool FileFd::Seek(unsigned long long To)
1255 {
1256 if (d->pipe == true)
1257 {
1258 // Our poor man seeking in pipes is costly, so try to avoid it
1259 unsigned long long seekpos = Tell();
1260 if (seekpos == To)
1261 return true;
1262 else if (seekpos < To)
1263 return Skip(To - seekpos);
1264
1265 if ((d->openmode & ReadOnly) != ReadOnly)
1266 return _error->Error("Reopen is only implemented for read-only files!");
1267 close(iFd);
1268 iFd = 0;
1269 if (TemporaryFileName.empty() == false)
1270 iFd = open(TemporaryFileName.c_str(), O_RDONLY);
1271 else if (FileName.empty() == false)
1272 iFd = open(FileName.c_str(), O_RDONLY);
1273 else
1274 {
1275 if (d->compressed_fd > 0)
1276 if (lseek(d->compressed_fd, 0, SEEK_SET) != 0)
1277 iFd = d->compressed_fd;
1278 if (iFd <= 0)
1279 return _error->Error("Reopen is not implemented for pipes opened with FileFd::OpenDescriptor()!");
1280 }
1281
1282 if (OpenInternDescriptor(d->openmode, d->compressor) == false)
1283 return _error->Error("Seek on file %s because it couldn't be reopened", FileName.c_str());
1284
1285 if (To != 0)
1286 return Skip(To);
1287
1288 d->seekpos = To;
1289 return true;
1290 }
1291 int res;
1292 #if APT_USE_ZLIB
1293 if (d->gz)
1294 res = gzseek(d->gz,To,SEEK_SET);
1295 else
1296 #endif
1297 res = lseek(iFd,To,SEEK_SET);
1298 if (res != (signed)To)
1299 {
1300 Flags |= Fail;
1301 return _error->Error("Unable to seek to %llu", To);
1302 }
1303
1304 d->seekpos = To;
1305 return true;
1306 }
1307 /*}}}*/
1308 // FileFd::Skip - Seek in the file /*{{{*/
1309 // ---------------------------------------------------------------------
1310 /* */
1311 bool FileFd::Skip(unsigned long long Over)
1312 {
1313 if (d->pipe == true)
1314 {
1315 d->seekpos += Over;
1316 char buffer[1024];
1317 while (Over != 0)
1318 {
1319 unsigned long long toread = std::min((unsigned long long) sizeof(buffer), Over);
1320 if (Read(buffer, toread) == false)
1321 return _error->Error("Unable to seek ahead %llu",Over);
1322 Over -= toread;
1323 }
1324 return true;
1325 }
1326
1327 int res;
1328 #if APT_USE_ZLIB
1329 if (d->gz != NULL)
1330 res = gzseek(d->gz,Over,SEEK_CUR);
1331 else
1332 #endif
1333 res = lseek(iFd,Over,SEEK_CUR);
1334 if (res < 0)
1335 {
1336 Flags |= Fail;
1337 return _error->Error("Unable to seek ahead %llu",Over);
1338 }
1339 d->seekpos = res;
1340
1341 return true;
1342 }
1343 /*}}}*/
1344 // FileFd::Truncate - Truncate the file /*{{{*/
1345 // ---------------------------------------------------------------------
1346 /* */
1347 bool FileFd::Truncate(unsigned long long To)
1348 {
1349 if (d->gz != NULL)
1350 {
1351 Flags |= Fail;
1352 return _error->Error("Truncating gzipped files is not implemented (%s)", FileName.c_str());
1353 }
1354 if (ftruncate(iFd,To) != 0)
1355 {
1356 Flags |= Fail;
1357 return _error->Error("Unable to truncate to %llu",To);
1358 }
1359
1360 return true;
1361 }
1362 /*}}}*/
1363 // FileFd::Tell - Current seek position /*{{{*/
1364 // ---------------------------------------------------------------------
1365 /* */
1366 unsigned long long FileFd::Tell()
1367 {
1368 // In theory, we could just return seekpos here always instead of
1369 // seeking around, but not all users of FileFd use always Seek() and co
1370 // so d->seekpos isn't always true and we can just use it as a hint if
1371 // we have nothing else, but not always as an authority…
1372 if (d->pipe == true)
1373 return d->seekpos;
1374
1375 off_t Res;
1376 #if APT_USE_ZLIB
1377 if (d->gz != NULL)
1378 Res = gztell(d->gz);
1379 else
1380 #endif
1381 Res = lseek(iFd,0,SEEK_CUR);
1382 if (Res == (off_t)-1)
1383 _error->Errno("lseek","Failed to determine the current file position");
1384 d->seekpos = Res;
1385 return Res;
1386 }
1387 /*}}}*/
1388 // FileFd::FileSize - Return the size of the file /*{{{*/
1389 // ---------------------------------------------------------------------
1390 /* */
1391 unsigned long long FileFd::FileSize()
1392 {
1393 struct stat Buf;
1394 if (d->pipe == false && fstat(iFd,&Buf) != 0)
1395 return _error->Errno("fstat","Unable to determine the file size");
1396
1397 // for compressor pipes st_size is undefined and at 'best' zero
1398 if (d->pipe == true || S_ISFIFO(Buf.st_mode))
1399 {
1400 // we set it here, too, as we get the info here for free
1401 // in theory the Open-methods should take care of it already
1402 d->pipe = true;
1403 if (stat(FileName.c_str(), &Buf) != 0)
1404 return _error->Errno("stat","Unable to determine the file size");
1405 }
1406
1407 return Buf.st_size;
1408 }
1409 /*}}}*/
1410 // FileFd::Size - Return the size of the content in the file /*{{{*/
1411 // ---------------------------------------------------------------------
1412 /* */
1413 unsigned long long FileFd::Size()
1414 {
1415 unsigned long long size = FileSize();
1416
1417 // for compressor pipes st_size is undefined and at 'best' zero,
1418 // so we 'read' the content and 'seek' back - see there
1419 if (d->pipe == true)
1420 {
1421 unsigned long long const oldSeek = Tell();
1422 char ignore[1000];
1423 unsigned long long read = 0;
1424 do {
1425 Read(ignore, sizeof(ignore), &read);
1426 } while(read != 0);
1427 size = Tell();
1428 Seek(oldSeek);
1429 }
1430 #if APT_USE_ZLIB
1431 // only check gzsize if we are actually a gzip file, just checking for
1432 // "gz" is not sufficient as uncompressed files could be opened with
1433 // gzopen in "direct" mode as well
1434 else if (d->gz && !gzdirect(d->gz) && size > 0)
1435 {
1436 off_t const oldPos = lseek(iFd,0,SEEK_CUR);
1437 /* unfortunately zlib.h doesn't provide a gzsize(), so we have to do
1438 * this ourselves; the original (uncompressed) file size is the last 32
1439 * bits of the file */
1440 // FIXME: Size for gz-files is limited by 32bit… no largefile support
1441 if (lseek(iFd, -4, SEEK_END) < 0)
1442 return _error->Errno("lseek","Unable to seek to end of gzipped file");
1443 size = 0L;
1444 if (read(iFd, &size, 4) != 4)
1445 return _error->Errno("read","Unable to read original size of gzipped file");
1446
1447 #ifdef WORDS_BIGENDIAN
1448 uint32_t tmp_size = size;
1449 uint8_t const * const p = (uint8_t const * const) &tmp_size;
1450 tmp_size = (p[3] << 24) | (p[2] << 16) | (p[1] << 8) | p[0];
1451 size = tmp_size;
1452 #endif
1453
1454 if (lseek(iFd, oldPos, SEEK_SET) < 0)
1455 return _error->Errno("lseek","Unable to seek in gzipped file");
1456
1457 return size;
1458 }
1459 #endif
1460
1461 return size;
1462 }
1463 /*}}}*/
1464 // FileFd::ModificationTime - Return the time of last touch /*{{{*/
1465 // ---------------------------------------------------------------------
1466 /* */
1467 time_t FileFd::ModificationTime()
1468 {
1469 struct stat Buf;
1470 if (d->pipe == false && fstat(iFd,&Buf) != 0)
1471 {
1472 _error->Errno("fstat","Unable to determine the modification time of file %s", FileName.c_str());
1473 return 0;
1474 }
1475
1476 // for compressor pipes st_size is undefined and at 'best' zero
1477 if (d->pipe == true || S_ISFIFO(Buf.st_mode))
1478 {
1479 // we set it here, too, as we get the info here for free
1480 // in theory the Open-methods should take care of it already
1481 d->pipe = true;
1482 if (stat(FileName.c_str(), &Buf) != 0)
1483 {
1484 _error->Errno("fstat","Unable to determine the modification time of file %s", FileName.c_str());
1485 return 0;
1486 }
1487 }
1488
1489 return Buf.st_mtime;
1490 }
1491 /*}}}*/
1492 // FileFd::Close - Close the file if the close flag is set /*{{{*/
1493 // ---------------------------------------------------------------------
1494 /* */
1495 bool FileFd::Close()
1496 {
1497 if (iFd == -1)
1498 return true;
1499
1500 bool Res = true;
1501 if ((Flags & AutoClose) == AutoClose)
1502 {
1503 #if APT_USE_ZLIB
1504 if (d != NULL && d->gz != NULL) {
1505 int const e = gzclose(d->gz);
1506 // gzdclose() on empty files always fails with "buffer error" here, ignore that
1507 if (e != 0 && e != Z_BUF_ERROR)
1508 Res &= _error->Errno("close",_("Problem closing the gzip file %s"), FileName.c_str());
1509 } else
1510 #endif
1511 if (iFd > 0 && close(iFd) != 0)
1512 Res &= _error->Errno("close",_("Problem closing the file %s"), FileName.c_str());
1513 }
1514
1515 if ((Flags & Replace) == Replace && iFd >= 0) {
1516 if (rename(TemporaryFileName.c_str(), FileName.c_str()) != 0)
1517 Res &= _error->Errno("rename",_("Problem renaming the file %s to %s"), TemporaryFileName.c_str(), FileName.c_str());
1518
1519 FileName = TemporaryFileName; // for the unlink() below.
1520 TemporaryFileName.clear();
1521 }
1522
1523 iFd = -1;
1524
1525 if ((Flags & Fail) == Fail && (Flags & DelOnFail) == DelOnFail &&
1526 FileName.empty() == false)
1527 if (unlink(FileName.c_str()) != 0)
1528 Res &= _error->WarningE("unlnk",_("Problem unlinking the file %s"), FileName.c_str());
1529
1530 if (d != NULL)
1531 {
1532 if (d->compressor_pid > 0)
1533 ExecWait(d->compressor_pid, "FileFdCompressor", true);
1534 delete d;
1535 d = NULL;
1536 }
1537
1538 return Res;
1539 }
1540 /*}}}*/
1541 // FileFd::Sync - Sync the file /*{{{*/
1542 // ---------------------------------------------------------------------
1543 /* */
1544 bool FileFd::Sync()
1545 {
1546 #ifdef _POSIX_SYNCHRONIZED_IO
1547 if (fsync(iFd) != 0)
1548 return _error->Errno("sync",_("Problem syncing the file"));
1549 #endif
1550 return true;
1551 }
1552 /*}}}*/
1553
1554 gzFile FileFd::gzFd() { return (gzFile) d->gz; }