]> git.saurik.com Git - apt.git/blame_incremental - apt-pkg/contrib/fileutl.cc
test/integration/test-ubuntu-bug-346386-apt-get-update-paywall: use downloadfile()
[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 lzma_stream tmp_stream = LZMA_STREAM_INIT;
1245 d->lzma->stream = tmp_stream;
1246
1247 if ((Mode & ReadWrite) == ReadWrite)
1248 return FileFdError("ReadWrite mode is not supported for file %s", FileName.c_str());
1249
1250 if ((Mode & WriteOnly) == WriteOnly)
1251 {
1252 if (compressor.Name == "xz")
1253 {
1254 if (lzma_easy_encoder(&d->lzma->stream, xzlevel, LZMA_CHECK_CRC32) != LZMA_OK)
1255 return false;
1256 }
1257 else
1258 {
1259 lzma_options_lzma options;
1260 lzma_lzma_preset(&options, xzlevel);
1261 if (lzma_alone_encoder(&d->lzma->stream, &options) != LZMA_OK)
1262 return false;
1263 }
1264 d->lzma->compressing = true;
1265 }
1266 else
1267 {
1268 if (compressor.Name == "xz")
1269 {
1270 if (lzma_auto_decoder(&d->lzma->stream, memlimit, 0) != LZMA_OK)
1271 return false;
1272 }
1273 else
1274 {
1275 if (lzma_alone_decoder(&d->lzma->stream, memlimit) != LZMA_OK)
1276 return false;
1277 }
1278 d->lzma->compressing = false;
1279 }
1280 }
1281#endif
1282 Flags |= Compressed;
1283 return true;
1284 }
1285#endif
1286
1287 // collect zombies here in case we reopen
1288 if (d->compressor_pid > 0)
1289 ExecWait(d->compressor_pid, "FileFdCompressor", true);
1290
1291 if ((Mode & ReadWrite) == ReadWrite)
1292 return FileFdError("ReadWrite mode is not supported for file %s", FileName.c_str());
1293
1294 bool const Comp = (Mode & WriteOnly) == WriteOnly;
1295 if (Comp == false)
1296 {
1297 // Handle 'decompression' of empty files
1298 struct stat Buf;
1299 fstat(iFd, &Buf);
1300 if (Buf.st_size == 0 && S_ISFIFO(Buf.st_mode) == false)
1301 return true;
1302
1303 // We don't need the file open - instead let the compressor open it
1304 // as he properly knows better how to efficiently read from 'his' file
1305 if (FileName.empty() == false)
1306 {
1307 close(iFd);
1308 iFd = -1;
1309 }
1310 }
1311
1312 // Create a data pipe
1313 int Pipe[2] = {-1,-1};
1314 if (pipe(Pipe) != 0)
1315 return FileFdErrno("pipe",_("Failed to create subprocess IPC"));
1316 for (int J = 0; J != 2; J++)
1317 SetCloseExec(Pipe[J],true);
1318
1319 d->compressed_fd = iFd;
1320 d->pipe = true;
1321
1322 if (Comp == true)
1323 iFd = Pipe[1];
1324 else
1325 iFd = Pipe[0];
1326
1327 // The child..
1328 d->compressor_pid = ExecFork();
1329 if (d->compressor_pid == 0)
1330 {
1331 if (Comp == true)
1332 {
1333 dup2(d->compressed_fd,STDOUT_FILENO);
1334 dup2(Pipe[0],STDIN_FILENO);
1335 }
1336 else
1337 {
1338 if (d->compressed_fd != -1)
1339 dup2(d->compressed_fd,STDIN_FILENO);
1340 dup2(Pipe[1],STDOUT_FILENO);
1341 }
1342 int const nullfd = open("/dev/null", O_WRONLY);
1343 if (nullfd != -1)
1344 {
1345 dup2(nullfd,STDERR_FILENO);
1346 close(nullfd);
1347 }
1348
1349 SetCloseExec(STDOUT_FILENO,false);
1350 SetCloseExec(STDIN_FILENO,false);
1351
1352 std::vector<char const*> Args;
1353 Args.push_back(compressor.Binary.c_str());
1354 std::vector<std::string> const * const addArgs =
1355 (Comp == true) ? &(compressor.CompressArgs) : &(compressor.UncompressArgs);
1356 for (std::vector<std::string>::const_iterator a = addArgs->begin();
1357 a != addArgs->end(); ++a)
1358 Args.push_back(a->c_str());
1359 if (Comp == false && FileName.empty() == false)
1360 {
1361 // commands not needing arguments, do not need to be told about using standard output
1362 // in reality, only testcases with tools like cat, rev, rot13, … are able to trigger this
1363 if (compressor.CompressArgs.empty() == false && compressor.UncompressArgs.empty() == false)
1364 Args.push_back("--stdout");
1365 if (TemporaryFileName.empty() == false)
1366 Args.push_back(TemporaryFileName.c_str());
1367 else
1368 Args.push_back(FileName.c_str());
1369 }
1370 Args.push_back(NULL);
1371
1372 execvp(Args[0],(char **)&Args[0]);
1373 cerr << _("Failed to exec compressor ") << Args[0] << endl;
1374 _exit(100);
1375 }
1376 if (Comp == true)
1377 close(Pipe[0]);
1378 else
1379 close(Pipe[1]);
1380
1381 return true;
1382}
1383 /*}}}*/
1384// FileFd::~File - Closes the file /*{{{*/
1385// ---------------------------------------------------------------------
1386/* If the proper modes are selected then we close the Fd and possibly
1387 unlink the file on error. */
1388FileFd::~FileFd()
1389{
1390 Close();
1391 if (d != NULL)
1392 d->CloseDown(FileName);
1393 delete d;
1394 d = NULL;
1395}
1396 /*}}}*/
1397// FileFd::Read - Read a bit of the file /*{{{*/
1398// ---------------------------------------------------------------------
1399/* We are careful to handle interruption by a signal while reading
1400 gracefully. */
1401bool FileFd::Read(void *To,unsigned long long Size,unsigned long long *Actual)
1402{
1403 ssize_t Res;
1404 errno = 0;
1405 if (Actual != 0)
1406 *Actual = 0;
1407 *((char *)To) = '\0';
1408 do
1409 {
1410 if (false)
1411 /* dummy so that the rest can be 'else if's */;
1412#ifdef HAVE_ZLIB
1413 else if (d != NULL && d->gz != NULL)
1414 Res = gzread(d->gz,To,Size);
1415#endif
1416#ifdef HAVE_BZ2
1417 else if (d != NULL && d->bz2 != NULL)
1418 Res = BZ2_bzread(d->bz2,To,Size);
1419#endif
1420#ifdef HAVE_LZMA
1421 else if (d != NULL && d->lzma != NULL)
1422 {
1423 if (d->lzma->eof == true)
1424 break;
1425
1426 d->lzma->stream.next_out = (uint8_t *) To;
1427 d->lzma->stream.avail_out = Size;
1428 if (d->lzma->stream.avail_in == 0)
1429 {
1430 d->lzma->stream.next_in = d->lzma->buffer;
1431 d->lzma->stream.avail_in = fread(d->lzma->buffer, 1, sizeof(d->lzma->buffer)/sizeof(d->lzma->buffer[0]), d->lzma->file);
1432 }
1433 d->lzma->err = lzma_code(&d->lzma->stream, LZMA_RUN);
1434 if (d->lzma->err == LZMA_STREAM_END)
1435 {
1436 d->lzma->eof = true;
1437 Res = Size - d->lzma->stream.avail_out;
1438 }
1439 else if (d->lzma->err != LZMA_OK)
1440 {
1441 Res = -1;
1442 errno = 0;
1443 }
1444 else
1445 {
1446 Res = Size - d->lzma->stream.avail_out;
1447 if (Res == 0)
1448 {
1449 // lzma run was okay, but produced no output…
1450 Res = -1;
1451 errno = EINTR;
1452 }
1453 }
1454 }
1455#endif
1456 else
1457 Res = read(iFd,To,Size);
1458
1459 if (Res < 0)
1460 {
1461 if (errno == EINTR)
1462 {
1463 // trick the while-loop into running again
1464 Res = 1;
1465 errno = 0;
1466 continue;
1467 }
1468 if (false)
1469 /* dummy so that the rest can be 'else if's */;
1470#ifdef HAVE_ZLIB
1471 else if (d != NULL && d->gz != NULL)
1472 {
1473 int err;
1474 char const * const errmsg = gzerror(d->gz, &err);
1475 if (err != Z_ERRNO)
1476 return FileFdError("gzread: %s (%d: %s)", _("Read error"), err, errmsg);
1477 }
1478#endif
1479#ifdef HAVE_BZ2
1480 else if (d != NULL && d->bz2 != NULL)
1481 {
1482 int err;
1483 char const * const errmsg = BZ2_bzerror(d->bz2, &err);
1484 if (err != BZ_IO_ERROR)
1485 return FileFdError("BZ2_bzread: %s (%d: %s)", _("Read error"), err, errmsg);
1486 }
1487#endif
1488#ifdef HAVE_LZMA
1489 else if (d != NULL && d->lzma != NULL)
1490 return FileFdError("lzma_read: %s (%d)", _("Read error"), d->lzma->err);
1491#endif
1492 return FileFdErrno("read",_("Read error"));
1493 }
1494
1495 To = (char *)To + Res;
1496 Size -= Res;
1497 if (d != NULL)
1498 d->seekpos += Res;
1499 if (Actual != 0)
1500 *Actual += Res;
1501 }
1502 while (Res > 0 && Size > 0);
1503
1504 if (Size == 0)
1505 return true;
1506
1507 // Eof handling
1508 if (Actual != 0)
1509 {
1510 Flags |= HitEof;
1511 return true;
1512 }
1513
1514 return FileFdError(_("read, still have %llu to read but none left"), Size);
1515}
1516 /*}}}*/
1517// FileFd::ReadLine - Read a complete line from the file /*{{{*/
1518// ---------------------------------------------------------------------
1519/* Beware: This method can be quiet slow for big buffers on UNcompressed
1520 files because of the naive implementation! */
1521char* FileFd::ReadLine(char *To, unsigned long long const Size)
1522{
1523 *To = '\0';
1524#ifdef HAVE_ZLIB
1525 if (d != NULL && d->gz != NULL)
1526 return gzgets(d->gz, To, Size);
1527#endif
1528
1529 unsigned long long read = 0;
1530 while ((Size - 1) != read)
1531 {
1532 unsigned long long done = 0;
1533 if (Read(To + read, 1, &done) == false)
1534 return NULL;
1535 if (done == 0)
1536 break;
1537 if (To[read++] == '\n')
1538 break;
1539 }
1540 if (read == 0)
1541 return NULL;
1542 To[read] = '\0';
1543 return To;
1544}
1545 /*}}}*/
1546// FileFd::Write - Write to the file /*{{{*/
1547// ---------------------------------------------------------------------
1548/* */
1549bool FileFd::Write(const void *From,unsigned long long Size)
1550{
1551 ssize_t Res;
1552 errno = 0;
1553 do
1554 {
1555 if (false)
1556 /* dummy so that the rest can be 'else if's */;
1557#ifdef HAVE_ZLIB
1558 else if (d != NULL && d->gz != NULL)
1559 Res = gzwrite(d->gz,From,Size);
1560#endif
1561#ifdef HAVE_BZ2
1562 else if (d != NULL && d->bz2 != NULL)
1563 Res = BZ2_bzwrite(d->bz2,(void*)From,Size);
1564#endif
1565#ifdef HAVE_LZMA
1566 else if (d != NULL && d->lzma != NULL)
1567 {
1568 d->lzma->stream.next_in = (uint8_t *)From;
1569 d->lzma->stream.avail_in = Size;
1570 d->lzma->stream.next_out = d->lzma->buffer;
1571 d->lzma->stream.avail_out = sizeof(d->lzma->buffer)/sizeof(d->lzma->buffer[0]);
1572 d->lzma->err = lzma_code(&d->lzma->stream, LZMA_RUN);
1573 if (d->lzma->err != LZMA_OK)
1574 return false;
1575 size_t const n = sizeof(d->lzma->buffer)/sizeof(d->lzma->buffer[0]) - d->lzma->stream.avail_out;
1576 size_t const m = (n == 0) ? 0 : fwrite(d->lzma->buffer, 1, n, d->lzma->file);
1577 if (m != n)
1578 Res = -1;
1579 else
1580 Res = Size - d->lzma->stream.avail_in;
1581 }
1582#endif
1583 else
1584 Res = write(iFd,From,Size);
1585
1586 if (Res < 0 && errno == EINTR)
1587 continue;
1588 if (Res < 0)
1589 {
1590 if (false)
1591 /* dummy so that the rest can be 'else if's */;
1592#ifdef HAVE_ZLIB
1593 else if (d != NULL && d->gz != NULL)
1594 {
1595 int err;
1596 char const * const errmsg = gzerror(d->gz, &err);
1597 if (err != Z_ERRNO)
1598 return FileFdError("gzwrite: %s (%d: %s)", _("Write error"), err, errmsg);
1599 }
1600#endif
1601#ifdef HAVE_BZ2
1602 else if (d != NULL && d->bz2 != NULL)
1603 {
1604 int err;
1605 char const * const errmsg = BZ2_bzerror(d->bz2, &err);
1606 if (err != BZ_IO_ERROR)
1607 return FileFdError("BZ2_bzwrite: %s (%d: %s)", _("Write error"), err, errmsg);
1608 }
1609#endif
1610#ifdef HAVE_LZMA
1611 else if (d != NULL && d->lzma != NULL)
1612 return FileFdErrno("lzma_fwrite", _("Write error"));
1613#endif
1614 return FileFdErrno("write",_("Write error"));
1615 }
1616
1617 From = (char const *)From + Res;
1618 Size -= Res;
1619 if (d != NULL)
1620 d->seekpos += Res;
1621 }
1622 while (Res > 0 && Size > 0);
1623
1624 if (Size == 0)
1625 return true;
1626
1627 return FileFdError(_("write, still have %llu to write but couldn't"), Size);
1628}
1629bool FileFd::Write(int Fd, const void *From, unsigned long long Size)
1630{
1631 ssize_t Res;
1632 errno = 0;
1633 do
1634 {
1635 Res = write(Fd,From,Size);
1636 if (Res < 0 && errno == EINTR)
1637 continue;
1638 if (Res < 0)
1639 return _error->Errno("write",_("Write error"));
1640
1641 From = (char const *)From + Res;
1642 Size -= Res;
1643 }
1644 while (Res > 0 && Size > 0);
1645
1646 if (Size == 0)
1647 return true;
1648
1649 return _error->Error(_("write, still have %llu to write but couldn't"), Size);
1650}
1651 /*}}}*/
1652// FileFd::Seek - Seek in the file /*{{{*/
1653// ---------------------------------------------------------------------
1654/* */
1655bool FileFd::Seek(unsigned long long To)
1656{
1657 Flags &= ~HitEof;
1658
1659 if (d != NULL && (d->pipe == true || d->InternalStream() == true))
1660 {
1661 // Our poor man seeking in pipes is costly, so try to avoid it
1662 unsigned long long seekpos = Tell();
1663 if (seekpos == To)
1664 return true;
1665 else if (seekpos < To)
1666 return Skip(To - seekpos);
1667
1668 if ((d->openmode & ReadOnly) != ReadOnly)
1669 return FileFdError("Reopen is only implemented for read-only files!");
1670 d->InternalClose(FileName);
1671 if (iFd != -1)
1672 close(iFd);
1673 iFd = -1;
1674 if (TemporaryFileName.empty() == false)
1675 iFd = open(TemporaryFileName.c_str(), O_RDONLY);
1676 else if (FileName.empty() == false)
1677 iFd = open(FileName.c_str(), O_RDONLY);
1678 else
1679 {
1680 if (d->compressed_fd > 0)
1681 if (lseek(d->compressed_fd, 0, SEEK_SET) != 0)
1682 iFd = d->compressed_fd;
1683 if (iFd < 0)
1684 return FileFdError("Reopen is not implemented for pipes opened with FileFd::OpenDescriptor()!");
1685 }
1686
1687 if (OpenInternDescriptor(d->openmode, d->compressor) == false)
1688 return FileFdError("Seek on file %s because it couldn't be reopened", FileName.c_str());
1689
1690 if (To != 0)
1691 return Skip(To);
1692
1693 d->seekpos = To;
1694 return true;
1695 }
1696 off_t res;
1697#ifdef HAVE_ZLIB
1698 if (d != NULL && d->gz)
1699 res = gzseek(d->gz,To,SEEK_SET);
1700 else
1701#endif
1702 res = lseek(iFd,To,SEEK_SET);
1703 if (res != (off_t)To)
1704 return FileFdError("Unable to seek to %llu", To);
1705
1706 if (d != NULL)
1707 d->seekpos = To;
1708 return true;
1709}
1710 /*}}}*/
1711// FileFd::Skip - Seek in the file /*{{{*/
1712// ---------------------------------------------------------------------
1713/* */
1714bool FileFd::Skip(unsigned long long Over)
1715{
1716 if (d != NULL && (d->pipe == true || d->InternalStream() == true))
1717 {
1718 char buffer[1024];
1719 while (Over != 0)
1720 {
1721 unsigned long long toread = std::min((unsigned long long) sizeof(buffer), Over);
1722 if (Read(buffer, toread) == false)
1723 return FileFdError("Unable to seek ahead %llu",Over);
1724 Over -= toread;
1725 }
1726 return true;
1727 }
1728
1729 off_t res;
1730#ifdef HAVE_ZLIB
1731 if (d != NULL && d->gz != NULL)
1732 res = gzseek(d->gz,Over,SEEK_CUR);
1733 else
1734#endif
1735 res = lseek(iFd,Over,SEEK_CUR);
1736 if (res < 0)
1737 return FileFdError("Unable to seek ahead %llu",Over);
1738 if (d != NULL)
1739 d->seekpos = res;
1740
1741 return true;
1742}
1743 /*}}}*/
1744// FileFd::Truncate - Truncate the file /*{{{*/
1745// ---------------------------------------------------------------------
1746/* */
1747bool FileFd::Truncate(unsigned long long To)
1748{
1749 // truncating /dev/null is always successful - as we get an error otherwise
1750 if (To == 0 && FileName == "/dev/null")
1751 return true;
1752#if defined HAVE_ZLIB || defined HAVE_BZ2 || defined HAVE_LZMA
1753 if (d != NULL && (d->InternalStream() == true
1754#ifdef HAVE_ZLIB
1755 || d->gz != NULL
1756#endif
1757 ))
1758 return FileFdError("Truncating compressed files is not implemented (%s)", FileName.c_str());
1759#endif
1760 if (ftruncate(iFd,To) != 0)
1761 return FileFdError("Unable to truncate to %llu",To);
1762
1763 return true;
1764}
1765 /*}}}*/
1766// FileFd::Tell - Current seek position /*{{{*/
1767// ---------------------------------------------------------------------
1768/* */
1769unsigned long long FileFd::Tell()
1770{
1771 // In theory, we could just return seekpos here always instead of
1772 // seeking around, but not all users of FileFd use always Seek() and co
1773 // so d->seekpos isn't always true and we can just use it as a hint if
1774 // we have nothing else, but not always as an authority…
1775 if (d != NULL && (d->pipe == true || d->InternalStream() == true))
1776 return d->seekpos;
1777
1778 off_t Res;
1779#ifdef HAVE_ZLIB
1780 if (d != NULL && d->gz != NULL)
1781 Res = gztell(d->gz);
1782 else
1783#endif
1784 Res = lseek(iFd,0,SEEK_CUR);
1785 if (Res == (off_t)-1)
1786 FileFdErrno("lseek","Failed to determine the current file position");
1787 if (d != NULL)
1788 d->seekpos = Res;
1789 return Res;
1790}
1791 /*}}}*/
1792static bool StatFileFd(char const * const msg, int const iFd, std::string const &FileName, struct stat &Buf, FileFdPrivate * const d) /*{{{*/
1793{
1794 bool ispipe = (d != NULL && d->pipe == true);
1795 if (ispipe == false)
1796 {
1797 if (fstat(iFd,&Buf) != 0)
1798 // higher-level code will generate more meaningful messages,
1799 // even translated this would be meaningless for users
1800 return _error->Errno("fstat", "Unable to determine %s for fd %i", msg, iFd);
1801 if (FileName.empty() == false)
1802 ispipe = S_ISFIFO(Buf.st_mode);
1803 }
1804
1805 // for compressor pipes st_size is undefined and at 'best' zero
1806 if (ispipe == true)
1807 {
1808 // we set it here, too, as we get the info here for free
1809 // in theory the Open-methods should take care of it already
1810 if (d != NULL)
1811 d->pipe = true;
1812 if (stat(FileName.c_str(), &Buf) != 0)
1813 return _error->Errno("fstat", "Unable to determine %s for file %s", msg, FileName.c_str());
1814 }
1815 return true;
1816}
1817 /*}}}*/
1818// FileFd::FileSize - Return the size of the file /*{{{*/
1819unsigned long long FileFd::FileSize()
1820{
1821 struct stat Buf;
1822 if (StatFileFd("file size", iFd, FileName, Buf, d) == false)
1823 {
1824 Flags |= Fail;
1825 return 0;
1826 }
1827 return Buf.st_size;
1828}
1829 /*}}}*/
1830// FileFd::ModificationTime - Return the time of last touch /*{{{*/
1831time_t FileFd::ModificationTime()
1832{
1833 struct stat Buf;
1834 if (StatFileFd("modification time", iFd, FileName, Buf, d) == false)
1835 {
1836 Flags |= Fail;
1837 return 0;
1838 }
1839 return Buf.st_mtime;
1840}
1841 /*}}}*/
1842// FileFd::Size - Return the size of the content in the file /*{{{*/
1843// ---------------------------------------------------------------------
1844/* */
1845unsigned long long FileFd::Size()
1846{
1847 unsigned long long size = FileSize();
1848
1849 // for compressor pipes st_size is undefined and at 'best' zero,
1850 // so we 'read' the content and 'seek' back - see there
1851 if (d != NULL && (d->pipe == true || (d->InternalStream() == true && size > 0)))
1852 {
1853 unsigned long long const oldSeek = Tell();
1854 char ignore[1000];
1855 unsigned long long read = 0;
1856 do {
1857 if (Read(ignore, sizeof(ignore), &read) == false)
1858 {
1859 Seek(oldSeek);
1860 return 0;
1861 }
1862 } while(read != 0);
1863 size = Tell();
1864 Seek(oldSeek);
1865 }
1866#ifdef HAVE_ZLIB
1867 // only check gzsize if we are actually a gzip file, just checking for
1868 // "gz" is not sufficient as uncompressed files could be opened with
1869 // gzopen in "direct" mode as well
1870 else if (d != NULL && d->gz && !gzdirect(d->gz) && size > 0)
1871 {
1872 off_t const oldPos = lseek(iFd,0,SEEK_CUR);
1873 /* unfortunately zlib.h doesn't provide a gzsize(), so we have to do
1874 * this ourselves; the original (uncompressed) file size is the last 32
1875 * bits of the file */
1876 // FIXME: Size for gz-files is limited by 32bit… no largefile support
1877 if (lseek(iFd, -4, SEEK_END) < 0)
1878 {
1879 FileFdErrno("lseek","Unable to seek to end of gzipped file");
1880 return 0;
1881 }
1882 uint32_t size = 0;
1883 if (read(iFd, &size, 4) != 4)
1884 {
1885 FileFdErrno("read","Unable to read original size of gzipped file");
1886 return 0;
1887 }
1888 size = le32toh(size);
1889
1890 if (lseek(iFd, oldPos, SEEK_SET) < 0)
1891 {
1892 FileFdErrno("lseek","Unable to seek in gzipped file");
1893 return 0;
1894 }
1895
1896 return size;
1897 }
1898#endif
1899
1900 return size;
1901}
1902 /*}}}*/
1903// FileFd::Close - Close the file if the close flag is set /*{{{*/
1904// ---------------------------------------------------------------------
1905/* */
1906bool FileFd::Close()
1907{
1908 if (iFd == -1)
1909 return true;
1910
1911 bool Res = true;
1912 if ((Flags & AutoClose) == AutoClose)
1913 {
1914 if ((Flags & Compressed) != Compressed && iFd > 0 && close(iFd) != 0)
1915 Res &= _error->Errno("close",_("Problem closing the file %s"), FileName.c_str());
1916
1917 if (d != NULL)
1918 {
1919 Res &= d->CloseDown(FileName);
1920 delete d;
1921 d = NULL;
1922 }
1923 }
1924
1925 if ((Flags & Replace) == Replace) {
1926 if (rename(TemporaryFileName.c_str(), FileName.c_str()) != 0)
1927 Res &= _error->Errno("rename",_("Problem renaming the file %s to %s"), TemporaryFileName.c_str(), FileName.c_str());
1928
1929 FileName = TemporaryFileName; // for the unlink() below.
1930 TemporaryFileName.clear();
1931 }
1932
1933 iFd = -1;
1934
1935 if ((Flags & Fail) == Fail && (Flags & DelOnFail) == DelOnFail &&
1936 FileName.empty() == false)
1937 if (unlink(FileName.c_str()) != 0)
1938 Res &= _error->WarningE("unlnk",_("Problem unlinking the file %s"), FileName.c_str());
1939
1940 if (Res == false)
1941 Flags |= Fail;
1942 return Res;
1943}
1944 /*}}}*/
1945// FileFd::Sync - Sync the file /*{{{*/
1946// ---------------------------------------------------------------------
1947/* */
1948bool FileFd::Sync()
1949{
1950 if (fsync(iFd) != 0)
1951 return FileFdErrno("sync",_("Problem syncing the file"));
1952 return true;
1953}
1954 /*}}}*/
1955// FileFd::FileFdErrno - set Fail and call _error->Errno *{{{*/
1956bool FileFd::FileFdErrno(const char *Function, const char *Description,...)
1957{
1958 Flags |= Fail;
1959 va_list args;
1960 size_t msgSize = 400;
1961 int const errsv = errno;
1962 while (true)
1963 {
1964 va_start(args,Description);
1965 if (_error->InsertErrno(GlobalError::ERROR, Function, Description, args, errsv, msgSize) == false)
1966 break;
1967 va_end(args);
1968 }
1969 return false;
1970}
1971 /*}}}*/
1972// FileFd::FileFdError - set Fail and call _error->Error *{{{*/
1973bool FileFd::FileFdError(const char *Description,...) {
1974 Flags |= Fail;
1975 va_list args;
1976 size_t msgSize = 400;
1977 while (true)
1978 {
1979 va_start(args,Description);
1980 if (_error->Insert(GlobalError::ERROR, Description, args, msgSize) == false)
1981 break;
1982 va_end(args);
1983 }
1984 return false;
1985}
1986 /*}}}*/
1987
1988APT_DEPRECATED gzFile FileFd::gzFd() {
1989#ifdef HAVE_ZLIB
1990 return d->gz;
1991#else
1992 return NULL;
1993#endif
1994}
1995
1996
1997// Glob - wrapper around "glob()" /*{{{*/
1998// ---------------------------------------------------------------------
1999/* */
2000std::vector<std::string> Glob(std::string const &pattern, int flags)
2001{
2002 std::vector<std::string> result;
2003 glob_t globbuf;
2004 int glob_res;
2005 unsigned int i;
2006
2007 glob_res = glob(pattern.c_str(), flags, NULL, &globbuf);
2008
2009 if (glob_res != 0)
2010 {
2011 if(glob_res != GLOB_NOMATCH) {
2012 _error->Errno("glob", "Problem with glob");
2013 return result;
2014 }
2015 }
2016
2017 // append results
2018 for(i=0;i<globbuf.gl_pathc;i++)
2019 result.push_back(string(globbuf.gl_pathv[i]));
2020
2021 globfree(&globbuf);
2022 return result;
2023}
2024 /*}}}*/
2025
2026std::string GetTempDir()
2027{
2028 const char *tmpdir = getenv("TMPDIR");
2029
2030#ifdef P_tmpdir
2031 if (!tmpdir)
2032 tmpdir = P_tmpdir;
2033#endif
2034
2035 // check that tmpdir is set and exists
2036 struct stat st;
2037 if (!tmpdir || strlen(tmpdir) == 0 || stat(tmpdir, &st) != 0)
2038 tmpdir = "/tmp";
2039
2040 return string(tmpdir);
2041}
2042
2043bool Rename(std::string From, std::string To)
2044{
2045 if (rename(From.c_str(),To.c_str()) != 0)
2046 {
2047 _error->Error(_("rename failed, %s (%s -> %s)."),strerror(errno),
2048 From.c_str(),To.c_str());
2049 return false;
2050 }
2051 return true;
2052}