Merge remote-tracking branch 'upstream/debian/sid' into debian/sid
[ntk/apt.git] / apt-pkg / contrib / fileutl.cc
... / ...
CommitLineData
1// -*- mode: cpp; mode: fold -*-
2// Description /*{{{*/
3/* ######################################################################
4
5 File Utilities
6
7 CopyFile - Buffered copy of a single file
8 GetLock - dpkg compatible lock file manipulation (fcntl)
9
10 Most of this source is placed in the Public Domain, do with it what
11 you will
12 It was originally written by Jason Gunthorpe <jgg@debian.org>.
13 FileFd gzip support added by Martin Pitt <martin.pitt@canonical.com>
14
15 The exception is RunScripts() it is under the GPLv2
16
17 ##################################################################### */
18 /*}}}*/
19// Include Files /*{{{*/
20#include <config.h>
21
22#include <apt-pkg/fileutl.h>
23#include <apt-pkg/strutl.h>
24#include <apt-pkg/error.h>
25#include <apt-pkg/sptr.h>
26#include <apt-pkg/aptconfiguration.h>
27#include <apt-pkg/configuration.h>
28#include <apt-pkg/macros.h>
29
30#include <ctype.h>
31#include <stdarg.h>
32#include <stddef.h>
33#include <sys/select.h>
34#include <time.h>
35#include <string>
36#include <vector>
37#include <cstdlib>
38#include <cstring>
39#include <cstdio>
40#include <iostream>
41#include <unistd.h>
42#include <fcntl.h>
43#include <sys/stat.h>
44#include <sys/time.h>
45#include <sys/wait.h>
46#include <dirent.h>
47#include <signal.h>
48#include <errno.h>
49#include <glob.h>
50
51#include <set>
52#include <algorithm>
53
54#ifdef HAVE_ZLIB
55 #include <zlib.h>
56#endif
57#ifdef HAVE_BZ2
58 #include <bzlib.h>
59#endif
60#ifdef HAVE_LZMA
61 #include <lzma.h>
62#endif
63#include <endian.h>
64#include <stdint.h>
65
66#include <apti18n.h>
67 /*}}}*/
68
69using namespace std;
70
71// RunScripts - Run a set of scripts from a configuration subtree /*{{{*/
72// ---------------------------------------------------------------------
73/* */
74bool RunScripts(const char *Cnf)
75{
76 Configuration::Item const *Opts = _config->Tree(Cnf);
77 if (Opts == 0 || Opts->Child == 0)
78 return true;
79 Opts = Opts->Child;
80
81 // Fork for running the system calls
82 pid_t Child = ExecFork();
83
84 // This is the child
85 if (Child == 0)
86 {
87 if (_config->FindDir("DPkg::Chroot-Directory","/") != "/")
88 {
89 std::cerr << "Chrooting into "
90 << _config->FindDir("DPkg::Chroot-Directory")
91 << std::endl;
92 if (chroot(_config->FindDir("DPkg::Chroot-Directory","/").c_str()) != 0)
93 _exit(100);
94 }
95
96 if (chdir("/tmp/") != 0)
97 _exit(100);
98
99 unsigned int Count = 1;
100 for (; Opts != 0; Opts = Opts->Next, Count++)
101 {
102 if (Opts->Value.empty() == true)
103 continue;
104
105 if(_config->FindB("Debug::RunScripts", false) == true)
106 std::clog << "Running external script: '"
107 << Opts->Value << "'" << std::endl;
108
109 if (system(Opts->Value.c_str()) != 0)
110 _exit(100+Count);
111 }
112 _exit(0);
113 }
114
115 // Wait for the child
116 int Status = 0;
117 while (waitpid(Child,&Status,0) != Child)
118 {
119 if (errno == EINTR)
120 continue;
121 return _error->Errno("waitpid","Couldn't wait for subprocess");
122 }
123
124 // Restore sig int/quit
125 signal(SIGQUIT,SIG_DFL);
126 signal(SIGINT,SIG_DFL);
127
128 // Check for an error code.
129 if (WIFEXITED(Status) == 0 || WEXITSTATUS(Status) != 0)
130 {
131 unsigned int Count = WEXITSTATUS(Status);
132 if (Count > 100)
133 {
134 Count -= 100;
135 for (; Opts != 0 && Count != 1; Opts = Opts->Next, Count--);
136 _error->Error("Problem executing scripts %s '%s'",Cnf,Opts->Value.c_str());
137 }
138
139 return _error->Error("Sub-process returned an error code");
140 }
141
142 return true;
143}
144 /*}}}*/
145
146// CopyFile - Buffered copy of a file /*{{{*/
147// ---------------------------------------------------------------------
148/* The caller is expected to set things so that failure causes erasure */
149bool CopyFile(FileFd &From,FileFd &To)
150{
151 if (From.IsOpen() == false || To.IsOpen() == false ||
152 From.Failed() == true || To.Failed() == true)
153 return false;
154
155 // Buffered copy between fds
156 SPtrArray<unsigned char> Buf = new unsigned char[64000];
157 unsigned long long Size = From.Size();
158 while (Size != 0)
159 {
160 unsigned long long ToRead = Size;
161 if (Size > 64000)
162 ToRead = 64000;
163
164 if (From.Read(Buf,ToRead) == false ||
165 To.Write(Buf,ToRead) == false)
166 return false;
167
168 Size -= ToRead;
169 }
170
171 return true;
172}
173 /*}}}*/
174// GetLock - Gets a lock file /*{{{*/
175// ---------------------------------------------------------------------
176/* This will create an empty file of the given name and lock it. Once this
177 is done all other calls to GetLock in any other process will fail with
178 -1. The return result is the fd of the file, the call should call
179 close at some time. */
180int GetLock(string File,bool Errors)
181{
182 // GetLock() is used in aptitude on directories with public-write access
183 // Use O_NOFOLLOW here to prevent symlink traversal attacks
184 int FD = open(File.c_str(),O_RDWR | O_CREAT | O_NOFOLLOW,0640);
185 if (FD < 0)
186 {
187 // Read only .. can't have locking problems there.
188 if (errno == EROFS)
189 {
190 _error->Warning(_("Not using locking for read only lock file %s"),File.c_str());
191 return dup(0); // Need something for the caller to close
192 }
193
194 if (Errors == true)
195 _error->Errno("open",_("Could not open lock file %s"),File.c_str());
196
197 // Feh.. We do this to distinguish the lock vs open case..
198 errno = EPERM;
199 return -1;
200 }
201 SetCloseExec(FD,true);
202
203 // Acquire a write lock
204 struct flock fl;
205 fl.l_type = F_WRLCK;
206 fl.l_whence = SEEK_SET;
207 fl.l_start = 0;
208 fl.l_len = 0;
209 if (fcntl(FD,F_SETLK,&fl) == -1)
210 {
211 // always close to not leak resources
212 int Tmp = errno;
213 close(FD);
214 errno = Tmp;
215
216 if (errno == ENOLCK)
217 {
218 _error->Warning(_("Not using locking for nfs mounted lock file %s"),File.c_str());
219 return dup(0); // Need something for the caller to close
220 }
221
222 if (Errors == true)
223 _error->Errno("open",_("Could not get lock %s"),File.c_str());
224
225 return -1;
226 }
227
228 return FD;
229}
230 /*}}}*/
231// FileExists - Check if a file exists /*{{{*/
232// ---------------------------------------------------------------------
233/* Beware: Directories are also files! */
234bool FileExists(string File)
235{
236 struct stat Buf;
237 if (stat(File.c_str(),&Buf) != 0)
238 return false;
239 return true;
240}
241 /*}}}*/
242// RealFileExists - Check if a file exists and if it is really a file /*{{{*/
243// ---------------------------------------------------------------------
244/* */
245bool RealFileExists(string File)
246{
247 struct stat Buf;
248 if (stat(File.c_str(),&Buf) != 0)
249 return false;
250 return ((Buf.st_mode & S_IFREG) != 0);
251}
252 /*}}}*/
253// DirectoryExists - Check if a directory exists and is really one /*{{{*/
254// ---------------------------------------------------------------------
255/* */
256bool DirectoryExists(string const &Path)
257{
258 struct stat Buf;
259 if (stat(Path.c_str(),&Buf) != 0)
260 return false;
261 return ((Buf.st_mode & S_IFDIR) != 0);
262}
263 /*}}}*/
264// CreateDirectory - poor man's mkdir -p guarded by a parent directory /*{{{*/
265// ---------------------------------------------------------------------
266/* This method will create all directories needed for path in good old
267 mkdir -p style but refuses to do this if Parent is not a prefix of
268 this Path. Example: /var/cache/ and /var/cache/apt/archives are given,
269 so it will create apt/archives if /var/cache exists - on the other
270 hand if the parent is /var/lib the creation will fail as this path
271 is not a parent of the path to be generated. */
272bool CreateDirectory(string const &Parent, string const &Path)
273{
274 if (Parent.empty() == true || Path.empty() == true)
275 return false;
276
277 if (DirectoryExists(Path) == true)
278 return true;
279
280 if (DirectoryExists(Parent) == false)
281 return false;
282
283 // we are not going to create directories "into the blue"
284 if (Path.compare(0, Parent.length(), Parent) != 0)
285 return false;
286
287 vector<string> const dirs = VectorizeString(Path.substr(Parent.size()), '/');
288 string progress = Parent;
289 for (vector<string>::const_iterator d = dirs.begin(); d != dirs.end(); ++d)
290 {
291 if (d->empty() == true)
292 continue;
293
294 progress.append("/").append(*d);
295 if (DirectoryExists(progress) == true)
296 continue;
297
298 if (mkdir(progress.c_str(), 0755) != 0)
299 return false;
300 }
301 return true;
302}
303 /*}}}*/
304// CreateAPTDirectoryIfNeeded - ensure that the given directory exists /*{{{*/
305// ---------------------------------------------------------------------
306/* a small wrapper around CreateDirectory to check if it exists and to
307 remove the trailing "/apt/" from the parent directory if needed */
308bool CreateAPTDirectoryIfNeeded(string const &Parent, string const &Path)
309{
310 if (DirectoryExists(Path) == true)
311 return true;
312
313 size_t const len = Parent.size();
314 if (len > 5 && Parent.find("/apt/", len - 6, 5) == len - 5)
315 {
316 if (CreateDirectory(Parent.substr(0,len-5), Path) == true)
317 return true;
318 }
319 else if (CreateDirectory(Parent, Path) == true)
320 return true;
321
322 return false;
323}
324 /*}}}*/
325// GetListOfFilesInDir - returns a vector of files in the given dir /*{{{*/
326// ---------------------------------------------------------------------
327/* If an extension is given only files with this extension are included
328 in the returned vector, otherwise every "normal" file is included. */
329std::vector<string> GetListOfFilesInDir(string const &Dir, string const &Ext,
330 bool const &SortList, bool const &AllowNoExt)
331{
332 std::vector<string> ext;
333 ext.reserve(2);
334 if (Ext.empty() == false)
335 ext.push_back(Ext);
336 if (AllowNoExt == true && ext.empty() == false)
337 ext.push_back("");
338 return GetListOfFilesInDir(Dir, ext, SortList);
339}
340std::vector<string> GetListOfFilesInDir(string const &Dir, std::vector<string> const &Ext,
341 bool const &SortList)
342{
343 // Attention debuggers: need to be set with the environment config file!
344 bool const Debug = _config->FindB("Debug::GetListOfFilesInDir", false);
345 if (Debug == true)
346 {
347 std::clog << "Accept in " << Dir << " only files with the following " << Ext.size() << " extensions:" << std::endl;
348 if (Ext.empty() == true)
349 std::clog << "\tNO extension" << std::endl;
350 else
351 for (std::vector<string>::const_iterator e = Ext.begin();
352 e != Ext.end(); ++e)
353 std::clog << '\t' << (e->empty() == true ? "NO" : *e) << " extension" << std::endl;
354 }
355
356 std::vector<string> List;
357
358 if (DirectoryExists(Dir) == false)
359 {
360 _error->Error(_("List of files can't be created as '%s' is not a directory"), Dir.c_str());
361 return List;
362 }
363
364 Configuration::MatchAgainstConfig SilentIgnore("Dir::Ignore-Files-Silently");
365 DIR *D = opendir(Dir.c_str());
366 if (D == 0)
367 {
368 _error->Errno("opendir",_("Unable to read %s"),Dir.c_str());
369 return List;
370 }
371
372 for (struct dirent *Ent = readdir(D); Ent != 0; Ent = readdir(D))
373 {
374 // skip "hidden" files
375 if (Ent->d_name[0] == '.')
376 continue;
377
378 // Make sure it is a file and not something else
379 string const File = flCombine(Dir,Ent->d_name);
380#ifdef _DIRENT_HAVE_D_TYPE
381 if (Ent->d_type != DT_REG)
382#endif
383 {
384 if (RealFileExists(File) == false)
385 {
386 // do not show ignoration warnings for directories
387 if (
388#ifdef _DIRENT_HAVE_D_TYPE
389 Ent->d_type == DT_DIR ||
390#endif
391 DirectoryExists(File) == true)
392 continue;
393 if (SilentIgnore.Match(Ent->d_name) == false)
394 _error->Notice(_("Ignoring '%s' in directory '%s' as it is not a regular file"), Ent->d_name, Dir.c_str());
395 continue;
396 }
397 }
398
399 // check for accepted extension:
400 // no extension given -> periods are bad as hell!
401 // extensions given -> "" extension allows no extension
402 if (Ext.empty() == false)
403 {
404 string d_ext = flExtension(Ent->d_name);
405 if (d_ext == Ent->d_name) // no extension
406 {
407 if (std::find(Ext.begin(), Ext.end(), "") == Ext.end())
408 {
409 if (Debug == true)
410 std::clog << "Bad file: " << Ent->d_name << " → no extension" << std::endl;
411 if (SilentIgnore.Match(Ent->d_name) == false)
412 _error->Notice(_("Ignoring file '%s' in directory '%s' as it has no filename extension"), Ent->d_name, Dir.c_str());
413 continue;
414 }
415 }
416 else if (std::find(Ext.begin(), Ext.end(), d_ext) == Ext.end())
417 {
418 if (Debug == true)
419 std::clog << "Bad file: " << Ent->d_name << " → bad extension »" << flExtension(Ent->d_name) << "«" << std::endl;
420 if (SilentIgnore.Match(Ent->d_name) == false)
421 _error->Notice(_("Ignoring file '%s' in directory '%s' as it has an invalid filename extension"), Ent->d_name, Dir.c_str());
422 continue;
423 }
424 }
425
426 // Skip bad filenames ala run-parts
427 const char *C = Ent->d_name;
428 for (; *C != 0; ++C)
429 if (isalpha(*C) == 0 && isdigit(*C) == 0
430 && *C != '_' && *C != '-' && *C != ':') {
431 // no required extension -> dot is a bad character
432 if (*C == '.' && Ext.empty() == false)
433 continue;
434 break;
435 }
436
437 // we don't reach the end of the name -> bad character included
438 if (*C != 0)
439 {
440 if (Debug == true)
441 std::clog << "Bad file: " << Ent->d_name << " → bad character »"
442 << *C << "« in filename (period allowed: " << (Ext.empty() ? "no" : "yes") << ")" << std::endl;
443 continue;
444 }
445
446 // skip filenames which end with a period. These are never valid
447 if (*(C - 1) == '.')
448 {
449 if (Debug == true)
450 std::clog << "Bad file: " << Ent->d_name << " → Period as last character" << std::endl;
451 continue;
452 }
453
454 if (Debug == true)
455 std::clog << "Accept file: " << Ent->d_name << " in " << Dir << std::endl;
456 List.push_back(File);
457 }
458 closedir(D);
459
460 if (SortList == true)
461 std::sort(List.begin(),List.end());
462 return List;
463}
464std::vector<string> GetListOfFilesInDir(string const &Dir, bool SortList)
465{
466 bool const Debug = _config->FindB("Debug::GetListOfFilesInDir", false);
467 if (Debug == true)
468 std::clog << "Accept in " << Dir << " all regular files" << std::endl;
469
470 std::vector<string> List;
471
472 if (DirectoryExists(Dir) == false)
473 {
474 _error->Error(_("List of files can't be created as '%s' is not a directory"), Dir.c_str());
475 return List;
476 }
477
478 DIR *D = opendir(Dir.c_str());
479 if (D == 0)
480 {
481 _error->Errno("opendir",_("Unable to read %s"),Dir.c_str());
482 return List;
483 }
484
485 for (struct dirent *Ent = readdir(D); Ent != 0; Ent = readdir(D))
486 {
487 // skip "hidden" files
488 if (Ent->d_name[0] == '.')
489 continue;
490
491 // Make sure it is a file and not something else
492 string const File = flCombine(Dir,Ent->d_name);
493#ifdef _DIRENT_HAVE_D_TYPE
494 if (Ent->d_type != DT_REG)
495#endif
496 {
497 if (RealFileExists(File) == false)
498 {
499 if (Debug == true)
500 std::clog << "Bad file: " << Ent->d_name << " → it is not a real file" << std::endl;
501 continue;
502 }
503 }
504
505 // Skip bad filenames ala run-parts
506 const char *C = Ent->d_name;
507 for (; *C != 0; ++C)
508 if (isalpha(*C) == 0 && isdigit(*C) == 0
509 && *C != '_' && *C != '-' && *C != '.')
510 break;
511
512 // we don't reach the end of the name -> bad character included
513 if (*C != 0)
514 {
515 if (Debug == true)
516 std::clog << "Bad file: " << Ent->d_name << " → bad character »" << *C << "« in filename" << std::endl;
517 continue;
518 }
519
520 // skip filenames which end with a period. These are never valid
521 if (*(C - 1) == '.')
522 {
523 if (Debug == true)
524 std::clog << "Bad file: " << Ent->d_name << " → Period as last character" << std::endl;
525 continue;
526 }
527
528 if (Debug == true)
529 std::clog << "Accept file: " << Ent->d_name << " in " << Dir << std::endl;
530 List.push_back(File);
531 }
532 closedir(D);
533
534 if (SortList == true)
535 std::sort(List.begin(),List.end());
536 return List;
537}
538 /*}}}*/
539// SafeGetCWD - This is a safer getcwd that returns a dynamic string /*{{{*/
540// ---------------------------------------------------------------------
541/* We return / on failure. */
542string SafeGetCWD()
543{
544 // Stash the current dir.
545 char S[300];
546 S[0] = 0;
547 if (getcwd(S,sizeof(S)-2) == 0)
548 return "/";
549 unsigned int Len = strlen(S);
550 S[Len] = '/';
551 S[Len+1] = 0;
552 return S;
553}
554 /*}}}*/
555// GetModificationTime - Get the mtime of the given file or -1 on error /*{{{*/
556// ---------------------------------------------------------------------
557/* We return / on failure. */
558time_t GetModificationTime(string const &Path)
559{
560 struct stat St;
561 if (stat(Path.c_str(), &St) < 0)
562 return -1;
563 return St.st_mtime;
564}
565 /*}}}*/
566// flNotDir - Strip the directory from the filename /*{{{*/
567// ---------------------------------------------------------------------
568/* */
569string flNotDir(string File)
570{
571 string::size_type Res = File.rfind('/');
572 if (Res == string::npos)
573 return File;
574 Res++;
575 return string(File,Res,Res - File.length());
576}
577 /*}}}*/
578// flNotFile - Strip the file from the directory name /*{{{*/
579// ---------------------------------------------------------------------
580/* Result ends in a / */
581string flNotFile(string File)
582{
583 string::size_type Res = File.rfind('/');
584 if (Res == string::npos)
585 return "./";
586 Res++;
587 return string(File,0,Res);
588}
589 /*}}}*/
590// flExtension - Return the extension for the file /*{{{*/
591// ---------------------------------------------------------------------
592/* */
593string flExtension(string File)
594{
595 string::size_type Res = File.rfind('.');
596 if (Res == string::npos)
597 return File;
598 Res++;
599 return string(File,Res,Res - File.length());
600}
601 /*}}}*/
602// flNoLink - If file is a symlink then deref it /*{{{*/
603// ---------------------------------------------------------------------
604/* If the name is not a link then the returned path is the input. */
605string flNoLink(string File)
606{
607 struct stat St;
608 if (lstat(File.c_str(),&St) != 0 || S_ISLNK(St.st_mode) == 0)
609 return File;
610 if (stat(File.c_str(),&St) != 0)
611 return File;
612
613 /* Loop resolving the link. There is no need to limit the number of
614 loops because the stat call above ensures that the symlink is not
615 circular */
616 char Buffer[1024];
617 string NFile = File;
618 while (1)
619 {
620 // Read the link
621 ssize_t Res;
622 if ((Res = readlink(NFile.c_str(),Buffer,sizeof(Buffer))) <= 0 ||
623 (size_t)Res >= sizeof(Buffer))
624 return File;
625
626 // Append or replace the previous path
627 Buffer[Res] = 0;
628 if (Buffer[0] == '/')
629 NFile = Buffer;
630 else
631 NFile = flNotFile(NFile) + Buffer;
632
633 // See if we are done
634 if (lstat(NFile.c_str(),&St) != 0)
635 return File;
636 if (S_ISLNK(St.st_mode) == 0)
637 return NFile;
638 }
639}
640 /*}}}*/
641// flCombine - Combine a file and a directory /*{{{*/
642// ---------------------------------------------------------------------
643/* If the file is an absolute path then it is just returned, otherwise
644 the directory is pre-pended to it. */
645string flCombine(string Dir,string File)
646{
647 if (File.empty() == true)
648 return string();
649
650 if (File[0] == '/' || Dir.empty() == true)
651 return File;
652 if (File.length() >= 2 && File[0] == '.' && File[1] == '/')
653 return File;
654 if (Dir[Dir.length()-1] == '/')
655 return Dir + File;
656 return Dir + '/' + File;
657}
658 /*}}}*/
659// SetCloseExec - Set the close on exec flag /*{{{*/
660// ---------------------------------------------------------------------
661/* */
662void SetCloseExec(int Fd,bool Close)
663{
664 if (fcntl(Fd,F_SETFD,(Close == false)?0:FD_CLOEXEC) != 0)
665 {
666 cerr << "FATAL -> Could not set close on exec " << strerror(errno) << endl;
667 exit(100);
668 }
669}
670 /*}}}*/
671// SetNonBlock - Set the nonblocking flag /*{{{*/
672// ---------------------------------------------------------------------
673/* */
674void SetNonBlock(int Fd,bool Block)
675{
676 int Flags = fcntl(Fd,F_GETFL) & (~O_NONBLOCK);
677 if (fcntl(Fd,F_SETFL,Flags | ((Block == false)?0:O_NONBLOCK)) != 0)
678 {
679 cerr << "FATAL -> Could not set non-blocking flag " << strerror(errno) << endl;
680 exit(100);
681 }
682}
683 /*}}}*/
684// WaitFd - Wait for a FD to become readable /*{{{*/
685// ---------------------------------------------------------------------
686/* This waits for a FD to become readable using select. It is useful for
687 applications making use of non-blocking sockets. The timeout is
688 in seconds. */
689bool WaitFd(int Fd,bool write,unsigned long timeout)
690{
691 fd_set Set;
692 struct timeval tv;
693 FD_ZERO(&Set);
694 FD_SET(Fd,&Set);
695 tv.tv_sec = timeout;
696 tv.tv_usec = 0;
697 if (write == true)
698 {
699 int Res;
700 do
701 {
702 Res = select(Fd+1,0,&Set,0,(timeout != 0?&tv:0));
703 }
704 while (Res < 0 && errno == EINTR);
705
706 if (Res <= 0)
707 return false;
708 }
709 else
710 {
711 int Res;
712 do
713 {
714 Res = select(Fd+1,&Set,0,0,(timeout != 0?&tv:0));
715 }
716 while (Res < 0 && errno == EINTR);
717
718 if (Res <= 0)
719 return false;
720 }
721
722 return true;
723}
724 /*}}}*/
725// MergeKeepFdsFromConfiguration - Merge APT::Keep-Fds configuration /*{{{*/
726// ---------------------------------------------------------------------
727/* This is used to merge the APT::Keep-Fds with the provided KeepFDs
728 * set.
729 */
730void MergeKeepFdsFromConfiguration(std::set<int> &KeepFDs)
731{
732 Configuration::Item const *Opts = _config->Tree("APT::Keep-Fds");
733 if (Opts != 0 && Opts->Child != 0)
734 {
735 Opts = Opts->Child;
736 for (; Opts != 0; Opts = Opts->Next)
737 {
738 if (Opts->Value.empty() == true)
739 continue;
740 int fd = atoi(Opts->Value.c_str());
741 KeepFDs.insert(fd);
742 }
743 }
744}
745 /*}}}*/
746// ExecFork - Magical fork that sanitizes the context before execing /*{{{*/
747// ---------------------------------------------------------------------
748/* This is used if you want to cleanse the environment for the forked
749 child, it fixes up the important signals and nukes all of the fds,
750 otherwise acts like normal fork. */
751pid_t ExecFork()
752{
753 set<int> KeepFDs;
754 // we need to merge the Keep-Fds as external tools like
755 // debconf-apt-progress use it
756 MergeKeepFdsFromConfiguration(KeepFDs);
757 return ExecFork(KeepFDs);
758}
759
760pid_t ExecFork(std::set<int> KeepFDs)
761{
762 // Fork off the process
763 pid_t Process = fork();
764 if (Process < 0)
765 {
766 cerr << "FATAL -> Failed to fork." << endl;
767 exit(100);
768 }
769
770 // Spawn the subprocess
771 if (Process == 0)
772 {
773 // Setup the signals
774 signal(SIGPIPE,SIG_DFL);
775 signal(SIGQUIT,SIG_DFL);
776 signal(SIGINT,SIG_DFL);
777 signal(SIGWINCH,SIG_DFL);
778 signal(SIGCONT,SIG_DFL);
779 signal(SIGTSTP,SIG_DFL);
780
781 // Close all of our FDs - just in case
782 for (int K = 3; K != sysconf(_SC_OPEN_MAX); K++)
783 {
784 if(KeepFDs.find(K) == KeepFDs.end())
785 fcntl(K,F_SETFD,FD_CLOEXEC);
786 }
787 }
788
789 return Process;
790}
791 /*}}}*/
792// ExecWait - Fancy waitpid /*{{{*/
793// ---------------------------------------------------------------------
794/* Waits for the given sub process. If Reap is set then no errors are
795 generated. Otherwise a failed subprocess will generate a proper descriptive
796 message */
797bool ExecWait(pid_t Pid,const char *Name,bool Reap)
798{
799 if (Pid <= 1)
800 return true;
801
802 // Wait and collect the error code
803 int Status;
804 while (waitpid(Pid,&Status,0) != Pid)
805 {
806 if (errno == EINTR)
807 continue;
808
809 if (Reap == true)
810 return false;
811
812 return _error->Error(_("Waited for %s but it wasn't there"),Name);
813 }
814
815
816 // Check for an error code.
817 if (WIFEXITED(Status) == 0 || WEXITSTATUS(Status) != 0)
818 {
819 if (Reap == true)
820 return false;
821 if (WIFSIGNALED(Status) != 0)
822 {
823 if( WTERMSIG(Status) == SIGSEGV)
824 return _error->Error(_("Sub-process %s received a segmentation fault."),Name);
825 else
826 return _error->Error(_("Sub-process %s received signal %u."),Name, WTERMSIG(Status));
827 }
828
829 if (WIFEXITED(Status) != 0)
830 return _error->Error(_("Sub-process %s returned an error code (%u)"),Name,WEXITSTATUS(Status));
831
832 return _error->Error(_("Sub-process %s exited unexpectedly"),Name);
833 }
834
835 return true;
836}
837 /*}}}*/
838
839class FileFdPrivate { /*{{{*/
840 public:
841#ifdef HAVE_ZLIB
842 gzFile gz;
843#endif
844#ifdef HAVE_BZ2
845 BZFILE* bz2;
846#endif
847#ifdef HAVE_LZMA
848 struct LZMAFILE {
849 FILE* file;
850 uint8_t buffer[4096];
851 lzma_stream stream;
852 lzma_ret err;
853 bool eof;
854 bool compressing;
855
856 LZMAFILE() : file(NULL), eof(false), compressing(false) {}
857 ~LZMAFILE() {
858 if (compressing == true)
859 {
860 for (;;) {
861 stream.avail_out = sizeof(buffer)/sizeof(buffer[0]);
862 stream.next_out = buffer;
863 err = lzma_code(&stream, LZMA_FINISH);
864 if (err != LZMA_OK && err != LZMA_STREAM_END)
865 {
866 _error->Error("~LZMAFILE: Compress finalisation failed");
867 break;
868 }
869 size_t const n = sizeof(buffer)/sizeof(buffer[0]) - stream.avail_out;
870 if (n && fwrite(buffer, 1, n, file) != n)
871 {
872 _error->Errno("~LZMAFILE",_("Write error"));
873 break;
874 }
875 if (err == LZMA_STREAM_END)
876 break;
877 }
878 }
879 lzma_end(&stream);
880 fclose(file);
881 }
882 };
883 LZMAFILE* lzma;
884#endif
885 int compressed_fd;
886 pid_t compressor_pid;
887 bool pipe;
888 APT::Configuration::Compressor compressor;
889 unsigned int openmode;
890 unsigned long long seekpos;
891 FileFdPrivate() :
892#ifdef HAVE_ZLIB
893 gz(NULL),
894#endif
895#ifdef HAVE_BZ2
896 bz2(NULL),
897#endif
898#ifdef HAVE_LZMA
899 lzma(NULL),
900#endif
901 compressed_fd(-1), compressor_pid(-1), pipe(false),
902 openmode(0), seekpos(0) {};
903 bool InternalClose(std::string const &FileName)
904 {
905 if (false)
906 /* dummy so that the rest can be 'else if's */;
907#ifdef HAVE_ZLIB
908 else if (gz != NULL) {
909 int const e = gzclose(gz);
910 gz = NULL;
911 // gzdclose() on empty files always fails with "buffer error" here, ignore that
912 if (e != 0 && e != Z_BUF_ERROR)
913 return _error->Errno("close",_("Problem closing the gzip file %s"), FileName.c_str());
914 }
915#endif
916#ifdef HAVE_BZ2
917 else if (bz2 != NULL) {
918 BZ2_bzclose(bz2);
919 bz2 = NULL;
920 }
921#endif
922#ifdef HAVE_LZMA
923 else if (lzma != NULL) {
924 delete lzma;
925 lzma = NULL;
926 }
927#endif
928 return true;
929 }
930 bool CloseDown(std::string const &FileName)
931 {
932 bool const Res = InternalClose(FileName);
933
934 if (compressor_pid > 0)
935 ExecWait(compressor_pid, "FileFdCompressor", true);
936 compressor_pid = -1;
937
938 return Res;
939 }
940 bool InternalStream() const {
941 return false
942#ifdef HAVE_BZ2
943 || bz2 != NULL
944#endif
945#ifdef HAVE_LZMA
946 || lzma != NULL
947#endif
948 ;
949 }
950
951
952 ~FileFdPrivate() { CloseDown(""); }
953};
954 /*}}}*/
955// FileFd::Open - Open a file /*{{{*/
956// ---------------------------------------------------------------------
957/* The most commonly used open mode combinations are given with Mode */
958bool FileFd::Open(string FileName,unsigned int const Mode,CompressMode Compress, unsigned long const AccessMode)
959{
960 if (Mode == ReadOnlyGzip)
961 return Open(FileName, ReadOnly, Gzip, AccessMode);
962
963 if (Compress == Auto && (Mode & WriteOnly) == WriteOnly)
964 return FileFdError("Autodetection on %s only works in ReadOnly openmode!", FileName.c_str());
965
966 std::vector<APT::Configuration::Compressor> const compressors = APT::Configuration::getCompressors();
967 std::vector<APT::Configuration::Compressor>::const_iterator compressor = compressors.begin();
968 if (Compress == Auto)
969 {
970 for (; compressor != compressors.end(); ++compressor)
971 {
972 std::string file = FileName + compressor->Extension;
973 if (FileExists(file) == false)
974 continue;
975 FileName = file;
976 break;
977 }
978 }
979 else if (Compress == Extension)
980 {
981 std::string::size_type const found = FileName.find_last_of('.');
982 std::string ext;
983 if (found != std::string::npos)
984 {
985 ext = FileName.substr(found);
986 if (ext == ".new" || ext == ".bak")
987 {
988 std::string::size_type const found2 = FileName.find_last_of('.', found - 1);
989 if (found2 != std::string::npos)
990 ext = FileName.substr(found2, found - found2);
991 else
992 ext.clear();
993 }
994 }
995 for (; compressor != compressors.end(); ++compressor)
996 if (ext == compressor->Extension)
997 break;
998 // no matching extension - assume uncompressed (imagine files like 'example.org_Packages')
999 if (compressor == compressors.end())
1000 for (compressor = compressors.begin(); compressor != compressors.end(); ++compressor)
1001 if (compressor->Name == ".")
1002 break;
1003 }
1004 else
1005 {
1006 std::string name;
1007 switch (Compress)
1008 {
1009 case None: name = "."; break;
1010 case Gzip: name = "gzip"; break;
1011 case Bzip2: name = "bzip2"; break;
1012 case Lzma: name = "lzma"; break;
1013 case Xz: name = "xz"; break;
1014 case Auto:
1015 case Extension:
1016 // Unreachable
1017 return FileFdError("Opening File %s in None, Auto or Extension should be already handled?!?", FileName.c_str());
1018 }
1019 for (; compressor != compressors.end(); ++compressor)
1020 if (compressor->Name == name)
1021 break;
1022 if (compressor == compressors.end())
1023 return FileFdError("Can't find a configured compressor %s for file %s", name.c_str(), FileName.c_str());
1024 }
1025
1026 if (compressor == compressors.end())
1027 return FileFdError("Can't find a match for specified compressor mode for file %s", FileName.c_str());
1028 return Open(FileName, Mode, *compressor, AccessMode);
1029}
1030bool FileFd::Open(string FileName,unsigned int const Mode,APT::Configuration::Compressor const &compressor, unsigned long const AccessMode)
1031{
1032 Close();
1033 Flags = AutoClose;
1034
1035 if ((Mode & WriteOnly) != WriteOnly && (Mode & (Atomic | Create | Empty | Exclusive)) != 0)
1036 return FileFdError("ReadOnly mode for %s doesn't accept additional flags!", FileName.c_str());
1037 if ((Mode & ReadWrite) == 0)
1038 return FileFdError("No openmode provided in FileFd::Open for %s", FileName.c_str());
1039
1040 if ((Mode & Atomic) == Atomic)
1041 {
1042 Flags |= Replace;
1043 }
1044 else if ((Mode & (Exclusive | Create)) == (Exclusive | Create))
1045 {
1046 // for atomic, this will be done by rename in Close()
1047 unlink(FileName.c_str());
1048 }
1049 if ((Mode & Empty) == Empty)
1050 {
1051 struct stat Buf;
1052 if (lstat(FileName.c_str(),&Buf) == 0 && S_ISLNK(Buf.st_mode))
1053 unlink(FileName.c_str());
1054 }
1055
1056 int fileflags = 0;
1057 #define if_FLAGGED_SET(FLAG, MODE) if ((Mode & FLAG) == FLAG) fileflags |= MODE
1058 if_FLAGGED_SET(ReadWrite, O_RDWR);
1059 else if_FLAGGED_SET(ReadOnly, O_RDONLY);
1060 else if_FLAGGED_SET(WriteOnly, O_WRONLY);
1061
1062 if_FLAGGED_SET(Create, O_CREAT);
1063 if_FLAGGED_SET(Empty, O_TRUNC);
1064 if_FLAGGED_SET(Exclusive, O_EXCL);
1065 #undef if_FLAGGED_SET
1066
1067 if ((Mode & Atomic) == Atomic)
1068 {
1069 char *name = strdup((FileName + ".XXXXXX").c_str());
1070
1071 if((iFd = mkstemp(name)) == -1)
1072 {
1073 free(name);
1074 return FileFdErrno("mkstemp", "Could not create temporary file for %s", FileName.c_str());
1075 }
1076
1077 TemporaryFileName = string(name);
1078 free(name);
1079
1080 // umask() will always set the umask and return the previous value, so
1081 // we first set the umask and then reset it to the old value
1082 mode_t const CurrentUmask = umask(0);
1083 umask(CurrentUmask);
1084 // calculate the actual file permissions (just like open/creat)
1085 mode_t const FilePermissions = (AccessMode & ~CurrentUmask);
1086
1087 if(fchmod(iFd, FilePermissions) == -1)
1088 return FileFdErrno("fchmod", "Could not change permissions for temporary file %s", TemporaryFileName.c_str());
1089 }
1090 else
1091 iFd = open(FileName.c_str(), fileflags, AccessMode);
1092
1093 this->FileName = FileName;
1094 if (iFd == -1 || OpenInternDescriptor(Mode, compressor) == false)
1095 {
1096 if (iFd != -1)
1097 {
1098 close (iFd);
1099 iFd = -1;
1100 }
1101 return FileFdErrno("open",_("Could not open file %s"), FileName.c_str());
1102 }
1103
1104 SetCloseExec(iFd,true);
1105 return true;
1106}
1107 /*}}}*/
1108// FileFd::OpenDescriptor - Open a filedescriptor /*{{{*/
1109// ---------------------------------------------------------------------
1110/* */
1111bool FileFd::OpenDescriptor(int Fd, unsigned int const Mode, CompressMode Compress, bool AutoClose)
1112{
1113 std::vector<APT::Configuration::Compressor> const compressors = APT::Configuration::getCompressors();
1114 std::vector<APT::Configuration::Compressor>::const_iterator compressor = compressors.begin();
1115 std::string name;
1116
1117 // compat with the old API
1118 if (Mode == ReadOnlyGzip && Compress == None)
1119 Compress = Gzip;
1120
1121 switch (Compress)
1122 {
1123 case None: name = "."; break;
1124 case Gzip: name = "gzip"; break;
1125 case Bzip2: name = "bzip2"; break;
1126 case Lzma: name = "lzma"; break;
1127 case Xz: name = "xz"; break;
1128 case Auto:
1129 case Extension:
1130 if (AutoClose == true && Fd != -1)
1131 close(Fd);
1132 return FileFdError("Opening Fd %d in Auto or Extension compression mode is not supported", Fd);
1133 }
1134 for (; compressor != compressors.end(); ++compressor)
1135 if (compressor->Name == name)
1136 break;
1137 if (compressor == compressors.end())
1138 {
1139 if (AutoClose == true && Fd != -1)
1140 close(Fd);
1141 return FileFdError("Can't find a configured compressor %s for file %s", name.c_str(), FileName.c_str());
1142 }
1143 return OpenDescriptor(Fd, Mode, *compressor, AutoClose);
1144}
1145bool FileFd::OpenDescriptor(int Fd, unsigned int const Mode, APT::Configuration::Compressor const &compressor, bool AutoClose)
1146{
1147 Close();
1148 Flags = (AutoClose) ? FileFd::AutoClose : 0;
1149 iFd = Fd;
1150 this->FileName = "";
1151 if (OpenInternDescriptor(Mode, compressor) == false)
1152 {
1153 if (iFd != -1 && (
1154 (Flags & Compressed) == Compressed ||
1155 AutoClose == true))
1156 {
1157 close (iFd);
1158 iFd = -1;
1159 }
1160 return FileFdError(_("Could not open file descriptor %d"), Fd);
1161 }
1162 return true;
1163}
1164bool FileFd::OpenInternDescriptor(unsigned int const Mode, APT::Configuration::Compressor const &compressor)
1165{
1166 if (iFd == -1)
1167 return false;
1168 if (compressor.Name == "." || compressor.Binary.empty() == true)
1169 return true;
1170
1171#if defined HAVE_ZLIB || defined HAVE_BZ2 || defined HAVE_LZMA
1172 // the API to open files is similar, so setup to avoid code duplicates later
1173 // and while at it ensure that we close before opening (if its a reopen)
1174 void* (*compress_open)(int, const char *) = NULL;
1175 if (false)
1176 /* dummy so that the rest can be 'else if's */;
1177#define APT_COMPRESS_INIT(NAME,OPEN) \
1178 else if (compressor.Name == NAME) \
1179 { \
1180 compress_open = (void*(*)(int, const char *)) OPEN; \
1181 if (d != NULL) d->InternalClose(FileName); \
1182 }
1183#ifdef HAVE_ZLIB
1184 APT_COMPRESS_INIT("gzip", gzdopen)
1185#endif
1186#ifdef HAVE_BZ2
1187 APT_COMPRESS_INIT("bzip2", BZ2_bzdopen)
1188#endif
1189#ifdef HAVE_LZMA
1190 APT_COMPRESS_INIT("xz", fdopen)
1191 APT_COMPRESS_INIT("lzma", fdopen)
1192#endif
1193#undef APT_COMPRESS_INIT
1194#endif
1195
1196 if (d == NULL)
1197 {
1198 d = new FileFdPrivate();
1199 d->openmode = Mode;
1200 d->compressor = compressor;
1201#if defined HAVE_ZLIB || defined HAVE_BZ2 || defined HAVE_LZMA
1202 if ((Flags & AutoClose) != AutoClose && compress_open != NULL)
1203 {
1204 // Need to duplicate fd here or gz/bz2 close for cleanup will close the fd as well
1205 int const internFd = dup(iFd);
1206 if (internFd == -1)
1207 return FileFdErrno("OpenInternDescriptor", _("Could not open file descriptor %d"), iFd);
1208 iFd = internFd;
1209 }
1210#endif
1211 }
1212
1213#if defined HAVE_ZLIB || defined HAVE_BZ2 || defined HAVE_LZMA
1214 if (compress_open != NULL)
1215 {
1216 void* compress_struct = NULL;
1217 if ((Mode & ReadWrite) == ReadWrite)
1218 compress_struct = compress_open(iFd, "r+");
1219 else if ((Mode & WriteOnly) == WriteOnly)
1220 compress_struct = compress_open(iFd, "w");
1221 else
1222 compress_struct = compress_open(iFd, "r");
1223 if (compress_struct == NULL)
1224 return false;
1225
1226 if (false)
1227 /* dummy so that the rest can be 'else if's */;
1228#ifdef HAVE_ZLIB
1229 else if (compressor.Name == "gzip")
1230 d->gz = (gzFile) compress_struct;
1231#endif
1232#ifdef HAVE_BZ2
1233 else if (compressor.Name == "bzip2")
1234 d->bz2 = (BZFILE*) compress_struct;
1235#endif
1236#ifdef HAVE_LZMA
1237 else if (compressor.Name == "xz" || compressor.Name == "lzma")
1238 {
1239 uint32_t const xzlevel = 6;
1240 uint64_t const memlimit = UINT64_MAX;
1241 if (d->lzma == NULL)
1242 d->lzma = new FileFdPrivate::LZMAFILE;
1243 d->lzma->file = (FILE*) compress_struct;
1244 d->lzma->stream = LZMA_STREAM_INIT;
1245
1246 if ((Mode & ReadWrite) == ReadWrite)
1247 return FileFdError("ReadWrite mode is not supported for file %s", FileName.c_str());
1248
1249 if ((Mode & WriteOnly) == WriteOnly)
1250 {
1251 if (compressor.Name == "xz")
1252 {
1253 if (lzma_easy_encoder(&d->lzma->stream, xzlevel, LZMA_CHECK_CRC32) != LZMA_OK)
1254 return false;
1255 }
1256 else
1257 {
1258 lzma_options_lzma options;
1259 lzma_lzma_preset(&options, xzlevel);
1260 if (lzma_alone_encoder(&d->lzma->stream, &options) != LZMA_OK)
1261 return false;
1262 }
1263 d->lzma->compressing = true;
1264 }
1265 else
1266 {
1267 if (compressor.Name == "xz")
1268 {
1269 if (lzma_auto_decoder(&d->lzma->stream, memlimit, 0) != LZMA_OK)
1270 return false;
1271 }
1272 else
1273 {
1274 if (lzma_alone_decoder(&d->lzma->stream, memlimit) != LZMA_OK)
1275 return false;
1276 }
1277 d->lzma->compressing = false;
1278 }
1279 }
1280#endif
1281 Flags |= Compressed;
1282 return true;
1283 }
1284#endif
1285
1286 // collect zombies here in case we reopen
1287 if (d->compressor_pid > 0)
1288 ExecWait(d->compressor_pid, "FileFdCompressor", true);
1289
1290 if ((Mode & ReadWrite) == ReadWrite)
1291 return FileFdError("ReadWrite mode is not supported for file %s", FileName.c_str());
1292
1293 bool const Comp = (Mode & WriteOnly) == WriteOnly;
1294 if (Comp == false)
1295 {
1296 // Handle 'decompression' of empty files
1297 struct stat Buf;
1298 fstat(iFd, &Buf);
1299 if (Buf.st_size == 0 && S_ISFIFO(Buf.st_mode) == false)
1300 return true;
1301
1302 // We don't need the file open - instead let the compressor open it
1303 // as he properly knows better how to efficiently read from 'his' file
1304 if (FileName.empty() == false)
1305 {
1306 close(iFd);
1307 iFd = -1;
1308 }
1309 }
1310
1311 // Create a data pipe
1312 int Pipe[2] = {-1,-1};
1313 if (pipe(Pipe) != 0)
1314 return FileFdErrno("pipe",_("Failed to create subprocess IPC"));
1315 for (int J = 0; J != 2; J++)
1316 SetCloseExec(Pipe[J],true);
1317
1318 d->compressed_fd = iFd;
1319 d->pipe = true;
1320
1321 if (Comp == true)
1322 iFd = Pipe[1];
1323 else
1324 iFd = Pipe[0];
1325
1326 // The child..
1327 d->compressor_pid = ExecFork();
1328 if (d->compressor_pid == 0)
1329 {
1330 if (Comp == true)
1331 {
1332 dup2(d->compressed_fd,STDOUT_FILENO);
1333 dup2(Pipe[0],STDIN_FILENO);
1334 }
1335 else
1336 {
1337 if (d->compressed_fd != -1)
1338 dup2(d->compressed_fd,STDIN_FILENO);
1339 dup2(Pipe[1],STDOUT_FILENO);
1340 }
1341 int const nullfd = open("/dev/null", O_WRONLY);
1342 if (nullfd != -1)
1343 {
1344 dup2(nullfd,STDERR_FILENO);
1345 close(nullfd);
1346 }
1347
1348 SetCloseExec(STDOUT_FILENO,false);
1349 SetCloseExec(STDIN_FILENO,false);
1350
1351 std::vector<char const*> Args;
1352 Args.push_back(compressor.Binary.c_str());
1353 std::vector<std::string> const * const addArgs =
1354 (Comp == true) ? &(compressor.CompressArgs) : &(compressor.UncompressArgs);
1355 for (std::vector<std::string>::const_iterator a = addArgs->begin();
1356 a != addArgs->end(); ++a)
1357 Args.push_back(a->c_str());
1358 if (Comp == false && FileName.empty() == false)
1359 {
1360 // commands not needing arguments, do not need to be told about using standard output
1361 // in reality, only testcases with tools like cat, rev, rot13, … are able to trigger this
1362 if (compressor.CompressArgs.empty() == false && compressor.UncompressArgs.empty() == false)
1363 Args.push_back("--stdout");
1364 if (TemporaryFileName.empty() == false)
1365 Args.push_back(TemporaryFileName.c_str());
1366 else
1367 Args.push_back(FileName.c_str());
1368 }
1369 Args.push_back(NULL);
1370
1371 execvp(Args[0],(char **)&Args[0]);
1372 cerr << _("Failed to exec compressor ") << Args[0] << endl;
1373 _exit(100);
1374 }
1375 if (Comp == true)
1376 close(Pipe[0]);
1377 else
1378 close(Pipe[1]);
1379
1380 return true;
1381}
1382 /*}}}*/
1383// FileFd::~File - Closes the file /*{{{*/
1384// ---------------------------------------------------------------------
1385/* If the proper modes are selected then we close the Fd and possibly
1386 unlink the file on error. */
1387FileFd::~FileFd()
1388{
1389 Close();
1390 if (d != NULL)
1391 d->CloseDown(FileName);
1392 delete d;
1393 d = NULL;
1394}
1395 /*}}}*/
1396// FileFd::Read - Read a bit of the file /*{{{*/
1397// ---------------------------------------------------------------------
1398/* We are careful to handle interruption by a signal while reading
1399 gracefully. */
1400bool FileFd::Read(void *To,unsigned long long Size,unsigned long long *Actual)
1401{
1402 ssize_t Res;
1403 errno = 0;
1404 if (Actual != 0)
1405 *Actual = 0;
1406 *((char *)To) = '\0';
1407 do
1408 {
1409 if (false)
1410 /* dummy so that the rest can be 'else if's */;
1411#ifdef HAVE_ZLIB
1412 else if (d != NULL && d->gz != NULL)
1413 Res = gzread(d->gz,To,Size);
1414#endif
1415#ifdef HAVE_BZ2
1416 else if (d != NULL && d->bz2 != NULL)
1417 Res = BZ2_bzread(d->bz2,To,Size);
1418#endif
1419#ifdef HAVE_LZMA
1420 else if (d != NULL && d->lzma != NULL)
1421 {
1422 if (d->lzma->eof == true)
1423 break;
1424
1425 d->lzma->stream.next_out = (uint8_t *) To;
1426 d->lzma->stream.avail_out = Size;
1427 if (d->lzma->stream.avail_in == 0)
1428 {
1429 d->lzma->stream.next_in = d->lzma->buffer;
1430 d->lzma->stream.avail_in = fread(d->lzma->buffer, 1, sizeof(d->lzma->buffer)/sizeof(d->lzma->buffer[0]), d->lzma->file);
1431 }
1432 d->lzma->err = lzma_code(&d->lzma->stream, LZMA_RUN);
1433 if (d->lzma->err == LZMA_STREAM_END)
1434 {
1435 d->lzma->eof = true;
1436 Res = Size - d->lzma->stream.avail_out;
1437 }
1438 else if (d->lzma->err != LZMA_OK)
1439 {
1440 Res = -1;
1441 errno = 0;
1442 }
1443 else
1444 {
1445 Res = Size - d->lzma->stream.avail_out;
1446 if (Res == 0)
1447 {
1448 // lzma run was okay, but produced no output…
1449 Res = -1;
1450 errno = EINTR;
1451 }
1452 }
1453 }
1454#endif
1455 else
1456 Res = read(iFd,To,Size);
1457
1458 if (Res < 0)
1459 {
1460 if (errno == EINTR)
1461 {
1462 // trick the while-loop into running again
1463 Res = 1;
1464 errno = 0;
1465 continue;
1466 }
1467 if (false)
1468 /* dummy so that the rest can be 'else if's */;
1469#ifdef HAVE_ZLIB
1470 else if (d != NULL && d->gz != NULL)
1471 {
1472 int err;
1473 char const * const errmsg = gzerror(d->gz, &err);
1474 if (err != Z_ERRNO)
1475 return FileFdError("gzread: %s (%d: %s)", _("Read error"), err, errmsg);
1476 }
1477#endif
1478#ifdef HAVE_BZ2
1479 else if (d != NULL && d->bz2 != NULL)
1480 {
1481 int err;
1482 char const * const errmsg = BZ2_bzerror(d->bz2, &err);
1483 if (err != BZ_IO_ERROR)
1484 return FileFdError("BZ2_bzread: %s (%d: %s)", _("Read error"), err, errmsg);
1485 }
1486#endif
1487#ifdef HAVE_LZMA
1488 else if (d != NULL && d->lzma != NULL)
1489 return FileFdError("lzma_read: %s (%d)", _("Read error"), d->lzma->err);
1490#endif
1491 return FileFdErrno("read",_("Read error"));
1492 }
1493
1494 To = (char *)To + Res;
1495 Size -= Res;
1496 if (d != NULL)
1497 d->seekpos += Res;
1498 if (Actual != 0)
1499 *Actual += Res;
1500 }
1501 while (Res > 0 && Size > 0);
1502
1503 if (Size == 0)
1504 return true;
1505
1506 // Eof handling
1507 if (Actual != 0)
1508 {
1509 Flags |= HitEof;
1510 return true;
1511 }
1512
1513 return FileFdError(_("read, still have %llu to read but none left"), Size);
1514}
1515 /*}}}*/
1516// FileFd::ReadLine - Read a complete line from the file /*{{{*/
1517// ---------------------------------------------------------------------
1518/* Beware: This method can be quiet slow for big buffers on UNcompressed
1519 files because of the naive implementation! */
1520char* FileFd::ReadLine(char *To, unsigned long long const Size)
1521{
1522 *To = '\0';
1523#ifdef HAVE_ZLIB
1524 if (d != NULL && d->gz != NULL)
1525 return gzgets(d->gz, To, Size);
1526#endif
1527
1528 unsigned long long read = 0;
1529 while ((Size - 1) != read)
1530 {
1531 unsigned long long done = 0;
1532 if (Read(To + read, 1, &done) == false)
1533 return NULL;
1534 if (done == 0)
1535 break;
1536 if (To[read++] == '\n')
1537 break;
1538 }
1539 if (read == 0)
1540 return NULL;
1541 To[read] = '\0';
1542 return To;
1543}
1544 /*}}}*/
1545// FileFd::Write - Write to the file /*{{{*/
1546// ---------------------------------------------------------------------
1547/* */
1548bool FileFd::Write(const void *From,unsigned long long Size)
1549{
1550 ssize_t Res;
1551 errno = 0;
1552 do
1553 {
1554 if (false)
1555 /* dummy so that the rest can be 'else if's */;
1556#ifdef HAVE_ZLIB
1557 else if (d != NULL && d->gz != NULL)
1558 Res = gzwrite(d->gz,From,Size);
1559#endif
1560#ifdef HAVE_BZ2
1561 else if (d != NULL && d->bz2 != NULL)
1562 Res = BZ2_bzwrite(d->bz2,(void*)From,Size);
1563#endif
1564#ifdef HAVE_LZMA
1565 else if (d != NULL && d->lzma != NULL)
1566 {
1567 d->lzma->stream.next_in = (uint8_t *)From;
1568 d->lzma->stream.avail_in = Size;
1569 d->lzma->stream.next_out = d->lzma->buffer;
1570 d->lzma->stream.avail_out = sizeof(d->lzma->buffer)/sizeof(d->lzma->buffer[0]);
1571 d->lzma->err = lzma_code(&d->lzma->stream, LZMA_RUN);
1572 if (d->lzma->err != LZMA_OK)
1573 return false;
1574 size_t const n = sizeof(d->lzma->buffer)/sizeof(d->lzma->buffer[0]) - d->lzma->stream.avail_out;
1575 size_t const m = (n == 0) ? 0 : fwrite(d->lzma->buffer, 1, n, d->lzma->file);
1576 if (m != n)
1577 Res = -1;
1578 else
1579 Res = Size - d->lzma->stream.avail_in;
1580 }
1581#endif
1582 else
1583 Res = write(iFd,From,Size);
1584
1585 if (Res < 0 && errno == EINTR)
1586 continue;
1587 if (Res < 0)
1588 {
1589 if (false)
1590 /* dummy so that the rest can be 'else if's */;
1591#ifdef HAVE_ZLIB
1592 else if (d != NULL && d->gz != NULL)
1593 {
1594 int err;
1595 char const * const errmsg = gzerror(d->gz, &err);
1596 if (err != Z_ERRNO)
1597 return FileFdError("gzwrite: %s (%d: %s)", _("Write error"), err, errmsg);
1598 }
1599#endif
1600#ifdef HAVE_BZ2
1601 else if (d != NULL && d->bz2 != NULL)
1602 {
1603 int err;
1604 char const * const errmsg = BZ2_bzerror(d->bz2, &err);
1605 if (err != BZ_IO_ERROR)
1606 return FileFdError("BZ2_bzwrite: %s (%d: %s)", _("Write error"), err, errmsg);
1607 }
1608#endif
1609#ifdef HAVE_LZMA
1610 else if (d != NULL && d->lzma != NULL)
1611 return FileFdErrno("lzma_fwrite", _("Write error"));
1612#endif
1613 return FileFdErrno("write",_("Write error"));
1614 }
1615
1616 From = (char const *)From + Res;
1617 Size -= Res;
1618 if (d != NULL)
1619 d->seekpos += Res;
1620 }
1621 while (Res > 0 && Size > 0);
1622
1623 if (Size == 0)
1624 return true;
1625
1626 return FileFdError(_("write, still have %llu to write but couldn't"), Size);
1627}
1628bool FileFd::Write(int Fd, const void *From, unsigned long long Size)
1629{
1630 ssize_t Res;
1631 errno = 0;
1632 do
1633 {
1634 Res = write(Fd,From,Size);
1635 if (Res < 0 && errno == EINTR)
1636 continue;
1637 if (Res < 0)
1638 return _error->Errno("write",_("Write error"));
1639
1640 From = (char const *)From + Res;
1641 Size -= Res;
1642 }
1643 while (Res > 0 && Size > 0);
1644
1645 if (Size == 0)
1646 return true;
1647
1648 return _error->Error(_("write, still have %llu to write but couldn't"), Size);
1649}
1650 /*}}}*/
1651// FileFd::Seek - Seek in the file /*{{{*/
1652// ---------------------------------------------------------------------
1653/* */
1654bool FileFd::Seek(unsigned long long To)
1655{
1656 Flags &= ~HitEof;
1657
1658 if (d != NULL && (d->pipe == true || d->InternalStream() == true))
1659 {
1660 // Our poor man seeking in pipes is costly, so try to avoid it
1661 unsigned long long seekpos = Tell();
1662 if (seekpos == To)
1663 return true;
1664 else if (seekpos < To)
1665 return Skip(To - seekpos);
1666
1667 if ((d->openmode & ReadOnly) != ReadOnly)
1668 return FileFdError("Reopen is only implemented for read-only files!");
1669 d->InternalClose(FileName);
1670 if (iFd != -1)
1671 close(iFd);
1672 iFd = -1;
1673 if (TemporaryFileName.empty() == false)
1674 iFd = open(TemporaryFileName.c_str(), O_RDONLY);
1675 else if (FileName.empty() == false)
1676 iFd = open(FileName.c_str(), O_RDONLY);
1677 else
1678 {
1679 if (d->compressed_fd > 0)
1680 if (lseek(d->compressed_fd, 0, SEEK_SET) != 0)
1681 iFd = d->compressed_fd;
1682 if (iFd < 0)
1683 return FileFdError("Reopen is not implemented for pipes opened with FileFd::OpenDescriptor()!");
1684 }
1685
1686 if (OpenInternDescriptor(d->openmode, d->compressor) == false)
1687 return FileFdError("Seek on file %s because it couldn't be reopened", FileName.c_str());
1688
1689 if (To != 0)
1690 return Skip(To);
1691
1692 d->seekpos = To;
1693 return true;
1694 }
1695 off_t res;
1696#ifdef HAVE_ZLIB
1697 if (d != NULL && d->gz)
1698 res = gzseek(d->gz,To,SEEK_SET);
1699 else
1700#endif
1701 res = lseek(iFd,To,SEEK_SET);
1702 if (res != (off_t)To)
1703 return FileFdError("Unable to seek to %llu", To);
1704
1705 if (d != NULL)
1706 d->seekpos = To;
1707 return true;
1708}
1709 /*}}}*/
1710// FileFd::Skip - Seek in the file /*{{{*/
1711// ---------------------------------------------------------------------
1712/* */
1713bool FileFd::Skip(unsigned long long Over)
1714{
1715 if (d != NULL && (d->pipe == true || d->InternalStream() == true))
1716 {
1717 char buffer[1024];
1718 while (Over != 0)
1719 {
1720 unsigned long long toread = std::min((unsigned long long) sizeof(buffer), Over);
1721 if (Read(buffer, toread) == false)
1722 return FileFdError("Unable to seek ahead %llu",Over);
1723 Over -= toread;
1724 }
1725 return true;
1726 }
1727
1728 off_t res;
1729#ifdef HAVE_ZLIB
1730 if (d != NULL && d->gz != NULL)
1731 res = gzseek(d->gz,Over,SEEK_CUR);
1732 else
1733#endif
1734 res = lseek(iFd,Over,SEEK_CUR);
1735 if (res < 0)
1736 return FileFdError("Unable to seek ahead %llu",Over);
1737 if (d != NULL)
1738 d->seekpos = res;
1739
1740 return true;
1741}
1742 /*}}}*/
1743// FileFd::Truncate - Truncate the file /*{{{*/
1744// ---------------------------------------------------------------------
1745/* */
1746bool FileFd::Truncate(unsigned long long To)
1747{
1748 // truncating /dev/null is always successful - as we get an error otherwise
1749 if (To == 0 && FileName == "/dev/null")
1750 return true;
1751#if defined HAVE_ZLIB || defined HAVE_BZ2 || defined HAVE_LZMA
1752 if (d != NULL && (d->InternalStream() == true
1753#ifdef HAVE_ZLIB
1754 || d->gz != NULL
1755#endif
1756 ))
1757 return FileFdError("Truncating compressed files is not implemented (%s)", FileName.c_str());
1758#endif
1759 if (ftruncate(iFd,To) != 0)
1760 return FileFdError("Unable to truncate to %llu",To);
1761
1762 return true;
1763}
1764 /*}}}*/
1765// FileFd::Tell - Current seek position /*{{{*/
1766// ---------------------------------------------------------------------
1767/* */
1768unsigned long long FileFd::Tell()
1769{
1770 // In theory, we could just return seekpos here always instead of
1771 // seeking around, but not all users of FileFd use always Seek() and co
1772 // so d->seekpos isn't always true and we can just use it as a hint if
1773 // we have nothing else, but not always as an authority…
1774 if (d != NULL && (d->pipe == true || d->InternalStream() == true))
1775 return d->seekpos;
1776
1777 off_t Res;
1778#ifdef HAVE_ZLIB
1779 if (d != NULL && d->gz != NULL)
1780 Res = gztell(d->gz);
1781 else
1782#endif
1783 Res = lseek(iFd,0,SEEK_CUR);
1784 if (Res == (off_t)-1)
1785 FileFdErrno("lseek","Failed to determine the current file position");
1786 if (d != NULL)
1787 d->seekpos = Res;
1788 return Res;
1789}
1790 /*}}}*/
1791static bool StatFileFd(char const * const msg, int const iFd, std::string const &FileName, struct stat &Buf, FileFdPrivate * const d) /*{{{*/
1792{
1793 bool ispipe = (d != NULL && d->pipe == true);
1794 if (ispipe == false)
1795 {
1796 if (fstat(iFd,&Buf) != 0)
1797 // higher-level code will generate more meaningful messages,
1798 // even translated this would be meaningless for users
1799 return _error->Errno("fstat", "Unable to determine %s for fd %i", msg, iFd);
1800 ispipe = S_ISFIFO(Buf.st_mode);
1801 }
1802
1803 // for compressor pipes st_size is undefined and at 'best' zero
1804 if (ispipe == true)
1805 {
1806 // we set it here, too, as we get the info here for free
1807 // in theory the Open-methods should take care of it already
1808 if (d != NULL)
1809 d->pipe = true;
1810 if (stat(FileName.c_str(), &Buf) != 0)
1811 return _error->Errno("fstat", "Unable to determine %s for file %s", msg, FileName.c_str());
1812 }
1813 return true;
1814}
1815 /*}}}*/
1816// FileFd::FileSize - Return the size of the file /*{{{*/
1817unsigned long long FileFd::FileSize()
1818{
1819 struct stat Buf;
1820 if (StatFileFd("file size", iFd, FileName, Buf, d) == false)
1821 {
1822 Flags |= Fail;
1823 return 0;
1824 }
1825 return Buf.st_size;
1826}
1827 /*}}}*/
1828// FileFd::ModificationTime - Return the time of last touch /*{{{*/
1829time_t FileFd::ModificationTime()
1830{
1831 struct stat Buf;
1832 if (StatFileFd("modification time", iFd, FileName, Buf, d) == false)
1833 {
1834 Flags |= Fail;
1835 return 0;
1836 }
1837 return Buf.st_mtime;
1838}
1839 /*}}}*/
1840// FileFd::Size - Return the size of the content in the file /*{{{*/
1841// ---------------------------------------------------------------------
1842/* */
1843unsigned long long FileFd::Size()
1844{
1845 unsigned long long size = FileSize();
1846
1847 // for compressor pipes st_size is undefined and at 'best' zero,
1848 // so we 'read' the content and 'seek' back - see there
1849 if (d != NULL && (d->pipe == true || (d->InternalStream() == true && size > 0)))
1850 {
1851 unsigned long long const oldSeek = Tell();
1852 char ignore[1000];
1853 unsigned long long read = 0;
1854 do {
1855 if (Read(ignore, sizeof(ignore), &read) == false)
1856 {
1857 Seek(oldSeek);
1858 return 0;
1859 }
1860 } while(read != 0);
1861 size = Tell();
1862 Seek(oldSeek);
1863 }
1864#ifdef HAVE_ZLIB
1865 // only check gzsize if we are actually a gzip file, just checking for
1866 // "gz" is not sufficient as uncompressed files could be opened with
1867 // gzopen in "direct" mode as well
1868 else if (d != NULL && d->gz && !gzdirect(d->gz) && size > 0)
1869 {
1870 off_t const oldPos = lseek(iFd,0,SEEK_CUR);
1871 /* unfortunately zlib.h doesn't provide a gzsize(), so we have to do
1872 * this ourselves; the original (uncompressed) file size is the last 32
1873 * bits of the file */
1874 // FIXME: Size for gz-files is limited by 32bit… no largefile support
1875 if (lseek(iFd, -4, SEEK_END) < 0)
1876 {
1877 FileFdErrno("lseek","Unable to seek to end of gzipped file");
1878 return 0;
1879 }
1880 uint32_t size = 0;
1881 if (read(iFd, &size, 4) != 4)
1882 {
1883 FileFdErrno("read","Unable to read original size of gzipped file");
1884 return 0;
1885 }
1886 size = le32toh(size);
1887
1888 if (lseek(iFd, oldPos, SEEK_SET) < 0)
1889 {
1890 FileFdErrno("lseek","Unable to seek in gzipped file");
1891 return 0;
1892 }
1893
1894 return size;
1895 }
1896#endif
1897
1898 return size;
1899}
1900 /*}}}*/
1901// FileFd::Close - Close the file if the close flag is set /*{{{*/
1902// ---------------------------------------------------------------------
1903/* */
1904bool FileFd::Close()
1905{
1906 if (iFd == -1)
1907 return true;
1908
1909 bool Res = true;
1910 if ((Flags & AutoClose) == AutoClose)
1911 {
1912 if ((Flags & Compressed) != Compressed && iFd > 0 && close(iFd) != 0)
1913 Res &= _error->Errno("close",_("Problem closing the file %s"), FileName.c_str());
1914
1915 if (d != NULL)
1916 {
1917 Res &= d->CloseDown(FileName);
1918 delete d;
1919 d = NULL;
1920 }
1921 }
1922
1923 if ((Flags & Replace) == Replace) {
1924 if (rename(TemporaryFileName.c_str(), FileName.c_str()) != 0)
1925 Res &= _error->Errno("rename",_("Problem renaming the file %s to %s"), TemporaryFileName.c_str(), FileName.c_str());
1926
1927 FileName = TemporaryFileName; // for the unlink() below.
1928 TemporaryFileName.clear();
1929 }
1930
1931 iFd = -1;
1932
1933 if ((Flags & Fail) == Fail && (Flags & DelOnFail) == DelOnFail &&
1934 FileName.empty() == false)
1935 if (unlink(FileName.c_str()) != 0)
1936 Res &= _error->WarningE("unlnk",_("Problem unlinking the file %s"), FileName.c_str());
1937
1938 if (Res == false)
1939 Flags |= Fail;
1940 return Res;
1941}
1942 /*}}}*/
1943// FileFd::Sync - Sync the file /*{{{*/
1944// ---------------------------------------------------------------------
1945/* */
1946bool FileFd::Sync()
1947{
1948 if (fsync(iFd) != 0)
1949 return FileFdErrno("sync",_("Problem syncing the file"));
1950 return true;
1951}
1952 /*}}}*/
1953// FileFd::FileFdErrno - set Fail and call _error->Errno *{{{*/
1954bool FileFd::FileFdErrno(const char *Function, const char *Description,...)
1955{
1956 Flags |= Fail;
1957 va_list args;
1958 size_t msgSize = 400;
1959 int const errsv = errno;
1960 while (true)
1961 {
1962 va_start(args,Description);
1963 if (_error->InsertErrno(GlobalError::ERROR, Function, Description, args, errsv, msgSize) == false)
1964 break;
1965 va_end(args);
1966 }
1967 return false;
1968}
1969 /*}}}*/
1970// FileFd::FileFdError - set Fail and call _error->Error *{{{*/
1971bool FileFd::FileFdError(const char *Description,...) {
1972 Flags |= Fail;
1973 va_list args;
1974 size_t msgSize = 400;
1975 while (true)
1976 {
1977 va_start(args,Description);
1978 if (_error->Insert(GlobalError::ERROR, Description, args, msgSize) == false)
1979 break;
1980 va_end(args);
1981 }
1982 return false;
1983}
1984 /*}}}*/
1985
1986APT_DEPRECATED gzFile FileFd::gzFd() {
1987#ifdef HAVE_ZLIB
1988 return d->gz;
1989#else
1990 return NULL;
1991#endif
1992}
1993
1994
1995// Glob - wrapper around "glob()" /*{{{*/
1996// ---------------------------------------------------------------------
1997/* */
1998std::vector<std::string> Glob(std::string const &pattern, int flags)
1999{
2000 std::vector<std::string> result;
2001 glob_t globbuf;
2002 int glob_res;
2003 unsigned int i;
2004
2005 glob_res = glob(pattern.c_str(), flags, NULL, &globbuf);
2006
2007 if (glob_res != 0)
2008 {
2009 if(glob_res != GLOB_NOMATCH) {
2010 _error->Errno("glob", "Problem with glob");
2011 return result;
2012 }
2013 }
2014
2015 // append results
2016 for(i=0;i<globbuf.gl_pathc;i++)
2017 result.push_back(string(globbuf.gl_pathv[i]));
2018
2019 globfree(&globbuf);
2020 return result;
2021}
2022 /*}}}*/
2023
2024std::string GetTempDir()
2025{
2026 const char *tmpdir = getenv("TMPDIR");
2027
2028#ifdef P_tmpdir
2029 if (!tmpdir)
2030 tmpdir = P_tmpdir;
2031#endif
2032
2033 // check that tmpdir is set and exists
2034 struct stat st;
2035 if (!tmpdir || strlen(tmpdir) == 0 || stat(tmpdir, &st) != 0)
2036 tmpdir = "/tmp";
2037
2038 return string(tmpdir);
2039}
2040
2041bool Rename(std::string From, std::string To)
2042{
2043 if (rename(From.c_str(),To.c_str()) != 0)
2044 {
2045 _error->Error(_("rename failed, %s (%s -> %s)."),strerror(errno),
2046 From.c_str(),To.c_str());
2047 return false;
2048 }
2049 return true;
2050}