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