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