]> git.saurik.com Git - apt.git/blob - apt-pkg/contrib/fileutl.cc
Merge branch 'debian/jessie' 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 #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
75 using namespace std;
76
77 // RunScripts - Run a set of scripts from a configuration subtree /*{{{*/
78 // ---------------------------------------------------------------------
79 /* */
80 bool 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 */
155 bool 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. */
186 int 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! */
240 bool 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 /* */
251 bool 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 /* */
262 bool 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. */
278 bool 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 */
314 bool 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. */
335 std::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 }
346 std::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 }
470 std::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. */
548 string 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. */
564 time_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 /* */
575 string 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 / */
587 string 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 /* */
599 string 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. */
611 string 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. */
651 string 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 /* */
668 string 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 /* */
684 void 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 /* */
696 void 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. */
711 bool 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 */
752 void 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. */
773 pid_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
782 pid_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 // Close all of our FDs - just in case
804 for (int K = 3; K != sysconf(_SC_OPEN_MAX); K++)
805 {
806 if(KeepFDs.find(K) == KeepFDs.end())
807 fcntl(K,F_SETFD,FD_CLOEXEC);
808 }
809 }
810
811 return Process;
812 }
813 /*}}}*/
814 // ExecWait - Fancy waitpid /*{{{*/
815 // ---------------------------------------------------------------------
816 /* Waits for the given sub process. If Reap is set then no errors are
817 generated. Otherwise a failed subprocess will generate a proper descriptive
818 message */
819 bool ExecWait(pid_t Pid,const char *Name,bool Reap)
820 {
821 if (Pid <= 1)
822 return true;
823
824 // Wait and collect the error code
825 int Status;
826 while (waitpid(Pid,&Status,0) != Pid)
827 {
828 if (errno == EINTR)
829 continue;
830
831 if (Reap == true)
832 return false;
833
834 return _error->Error(_("Waited for %s but it wasn't there"),Name);
835 }
836
837
838 // Check for an error code.
839 if (WIFEXITED(Status) == 0 || WEXITSTATUS(Status) != 0)
840 {
841 if (Reap == true)
842 return false;
843 if (WIFSIGNALED(Status) != 0)
844 {
845 if( WTERMSIG(Status) == SIGSEGV)
846 return _error->Error(_("Sub-process %s received a segmentation fault."),Name);
847 else
848 return _error->Error(_("Sub-process %s received signal %u."),Name, WTERMSIG(Status));
849 }
850
851 if (WIFEXITED(Status) != 0)
852 return _error->Error(_("Sub-process %s returned an error code (%u)"),Name,WEXITSTATUS(Status));
853
854 return _error->Error(_("Sub-process %s exited unexpectedly"),Name);
855 }
856
857 return true;
858 }
859 /*}}}*/
860 // StartsWithGPGClearTextSignature - Check if a file is Pgp/GPG clearsigned /*{{{*/
861 bool StartsWithGPGClearTextSignature(string const &FileName)
862 {
863 static const char* SIGMSG = "-----BEGIN PGP SIGNED MESSAGE-----\n";
864 char buffer[strlen(SIGMSG)+1];
865 FILE* gpg = fopen(FileName.c_str(), "r");
866 if (gpg == NULL)
867 return false;
868
869 char const * const test = fgets(buffer, sizeof(buffer), gpg);
870 fclose(gpg);
871 if (test == NULL || strcmp(buffer, SIGMSG) != 0)
872 return false;
873
874 return true;
875 }
876 /*}}}*/
877 // ChangeOwnerAndPermissionOfFile - set file attributes to requested values /*{{{*/
878 bool ChangeOwnerAndPermissionOfFile(char const * const requester, char const * const file, char const * const user, char const * const group, mode_t const mode)
879 {
880 if (strcmp(file, "/dev/null") == 0)
881 return true;
882 bool Res = true;
883 if (getuid() == 0 && strlen(user) != 0 && strlen(group) != 0) // if we aren't root, we can't chown, so don't try it
884 {
885 // ensure the file is owned by root and has good permissions
886 struct passwd const * const pw = getpwnam(user);
887 struct group const * const gr = getgrnam(group);
888 if (pw != NULL && gr != NULL && chown(file, pw->pw_uid, gr->gr_gid) != 0)
889 Res &= _error->WarningE(requester, "chown to %s:%s of file %s failed", user, group, file);
890 }
891 if (chmod(file, mode) != 0)
892 Res &= _error->WarningE(requester, "chmod 0%o of file %s failed", mode, file);
893 return Res;
894 }
895 /*}}}*/
896
897 class FileFdPrivate { /*{{{*/
898 public:
899 #ifdef HAVE_ZLIB
900 gzFile gz;
901 #endif
902 #ifdef HAVE_BZ2
903 BZFILE* bz2;
904 #endif
905 #ifdef HAVE_LZMA
906 struct LZMAFILE {
907 FILE* file;
908 uint8_t buffer[4096];
909 lzma_stream stream;
910 lzma_ret err;
911 bool eof;
912 bool compressing;
913
914 LZMAFILE() : file(NULL), eof(false), compressing(false) { buffer[0] = '\0'; }
915 ~LZMAFILE() {
916 if (compressing == true)
917 {
918 for (;;) {
919 stream.avail_out = sizeof(buffer)/sizeof(buffer[0]);
920 stream.next_out = buffer;
921 err = lzma_code(&stream, LZMA_FINISH);
922 if (err != LZMA_OK && err != LZMA_STREAM_END)
923 {
924 _error->Error("~LZMAFILE: Compress finalisation failed");
925 break;
926 }
927 size_t const n = sizeof(buffer)/sizeof(buffer[0]) - stream.avail_out;
928 if (n && fwrite(buffer, 1, n, file) != n)
929 {
930 _error->Errno("~LZMAFILE",_("Write error"));
931 break;
932 }
933 if (err == LZMA_STREAM_END)
934 break;
935 }
936 }
937 lzma_end(&stream);
938 fclose(file);
939 }
940 };
941 LZMAFILE* lzma;
942 #endif
943 int compressed_fd;
944 pid_t compressor_pid;
945 bool pipe;
946 APT::Configuration::Compressor compressor;
947 unsigned int openmode;
948 unsigned long long seekpos;
949 FileFdPrivate() :
950 #ifdef HAVE_ZLIB
951 gz(NULL),
952 #endif
953 #ifdef HAVE_BZ2
954 bz2(NULL),
955 #endif
956 #ifdef HAVE_LZMA
957 lzma(NULL),
958 #endif
959 compressed_fd(-1), compressor_pid(-1), pipe(false),
960 openmode(0), seekpos(0) {};
961 bool InternalClose(std::string const &FileName)
962 {
963 if (false)
964 /* dummy so that the rest can be 'else if's */;
965 #ifdef HAVE_ZLIB
966 else if (gz != NULL) {
967 int const e = gzclose(gz);
968 gz = NULL;
969 // gzdclose() on empty files always fails with "buffer error" here, ignore that
970 if (e != 0 && e != Z_BUF_ERROR)
971 return _error->Errno("close",_("Problem closing the gzip file %s"), FileName.c_str());
972 }
973 #endif
974 #ifdef HAVE_BZ2
975 else if (bz2 != NULL) {
976 BZ2_bzclose(bz2);
977 bz2 = NULL;
978 }
979 #endif
980 #ifdef HAVE_LZMA
981 else if (lzma != NULL) {
982 delete lzma;
983 lzma = NULL;
984 }
985 #endif
986 return true;
987 }
988 bool CloseDown(std::string const &FileName)
989 {
990 bool const Res = InternalClose(FileName);
991
992 if (compressor_pid > 0)
993 ExecWait(compressor_pid, "FileFdCompressor", true);
994 compressor_pid = -1;
995
996 return Res;
997 }
998 bool InternalStream() const {
999 return false
1000 #ifdef HAVE_BZ2
1001 || bz2 != NULL
1002 #endif
1003 #ifdef HAVE_LZMA
1004 || lzma != NULL
1005 #endif
1006 ;
1007 }
1008
1009
1010 ~FileFdPrivate() { CloseDown(""); }
1011 };
1012 /*}}}*/
1013 // FileFd::Open - Open a file /*{{{*/
1014 // ---------------------------------------------------------------------
1015 /* The most commonly used open mode combinations are given with Mode */
1016 bool FileFd::Open(string FileName,unsigned int const Mode,CompressMode Compress, unsigned long const AccessMode)
1017 {
1018 if (Mode == ReadOnlyGzip)
1019 return Open(FileName, ReadOnly, Gzip, AccessMode);
1020
1021 if (Compress == Auto && (Mode & WriteOnly) == WriteOnly)
1022 return FileFdError("Autodetection on %s only works in ReadOnly openmode!", FileName.c_str());
1023
1024 std::vector<APT::Configuration::Compressor> const compressors = APT::Configuration::getCompressors();
1025 std::vector<APT::Configuration::Compressor>::const_iterator compressor = compressors.begin();
1026 if (Compress == Auto)
1027 {
1028 for (; compressor != compressors.end(); ++compressor)
1029 {
1030 std::string file = FileName + compressor->Extension;
1031 if (FileExists(file) == false)
1032 continue;
1033 FileName = file;
1034 break;
1035 }
1036 }
1037 else if (Compress == Extension)
1038 {
1039 std::string::size_type const found = FileName.find_last_of('.');
1040 std::string ext;
1041 if (found != std::string::npos)
1042 {
1043 ext = FileName.substr(found);
1044 if (ext == ".new" || ext == ".bak")
1045 {
1046 std::string::size_type const found2 = FileName.find_last_of('.', found - 1);
1047 if (found2 != std::string::npos)
1048 ext = FileName.substr(found2, found - found2);
1049 else
1050 ext.clear();
1051 }
1052 }
1053 for (; compressor != compressors.end(); ++compressor)
1054 if (ext == compressor->Extension)
1055 break;
1056 // no matching extension - assume uncompressed (imagine files like 'example.org_Packages')
1057 if (compressor == compressors.end())
1058 for (compressor = compressors.begin(); compressor != compressors.end(); ++compressor)
1059 if (compressor->Name == ".")
1060 break;
1061 }
1062 else
1063 {
1064 std::string name;
1065 switch (Compress)
1066 {
1067 case None: name = "."; break;
1068 case Gzip: name = "gzip"; break;
1069 case Bzip2: name = "bzip2"; break;
1070 case Lzma: name = "lzma"; break;
1071 case Xz: name = "xz"; break;
1072 case Auto:
1073 case Extension:
1074 // Unreachable
1075 return FileFdError("Opening File %s in None, Auto or Extension should be already handled?!?", FileName.c_str());
1076 }
1077 for (; compressor != compressors.end(); ++compressor)
1078 if (compressor->Name == name)
1079 break;
1080 if (compressor == compressors.end())
1081 return FileFdError("Can't find a configured compressor %s for file %s", name.c_str(), FileName.c_str());
1082 }
1083
1084 if (compressor == compressors.end())
1085 return FileFdError("Can't find a match for specified compressor mode for file %s", FileName.c_str());
1086 return Open(FileName, Mode, *compressor, AccessMode);
1087 }
1088 bool FileFd::Open(string FileName,unsigned int const Mode,APT::Configuration::Compressor const &compressor, unsigned long const AccessMode)
1089 {
1090 Close();
1091 Flags = AutoClose;
1092
1093 if ((Mode & WriteOnly) != WriteOnly && (Mode & (Atomic | Create | Empty | Exclusive)) != 0)
1094 return FileFdError("ReadOnly mode for %s doesn't accept additional flags!", FileName.c_str());
1095 if ((Mode & ReadWrite) == 0)
1096 return FileFdError("No openmode provided in FileFd::Open for %s", FileName.c_str());
1097
1098 if ((Mode & Atomic) == Atomic)
1099 {
1100 Flags |= Replace;
1101 }
1102 else if ((Mode & (Exclusive | Create)) == (Exclusive | Create))
1103 {
1104 // for atomic, this will be done by rename in Close()
1105 unlink(FileName.c_str());
1106 }
1107 if ((Mode & Empty) == Empty)
1108 {
1109 struct stat Buf;
1110 if (lstat(FileName.c_str(),&Buf) == 0 && S_ISLNK(Buf.st_mode))
1111 unlink(FileName.c_str());
1112 }
1113
1114 int fileflags = 0;
1115 #define if_FLAGGED_SET(FLAG, MODE) if ((Mode & FLAG) == FLAG) fileflags |= MODE
1116 if_FLAGGED_SET(ReadWrite, O_RDWR);
1117 else if_FLAGGED_SET(ReadOnly, O_RDONLY);
1118 else if_FLAGGED_SET(WriteOnly, O_WRONLY);
1119
1120 if_FLAGGED_SET(Create, O_CREAT);
1121 if_FLAGGED_SET(Empty, O_TRUNC);
1122 if_FLAGGED_SET(Exclusive, O_EXCL);
1123 #undef if_FLAGGED_SET
1124
1125 if ((Mode & Atomic) == Atomic)
1126 {
1127 char *name = strdup((FileName + ".XXXXXX").c_str());
1128
1129 if((iFd = mkstemp(name)) == -1)
1130 {
1131 free(name);
1132 return FileFdErrno("mkstemp", "Could not create temporary file for %s", FileName.c_str());
1133 }
1134
1135 TemporaryFileName = string(name);
1136 free(name);
1137
1138 // umask() will always set the umask and return the previous value, so
1139 // we first set the umask and then reset it to the old value
1140 mode_t const CurrentUmask = umask(0);
1141 umask(CurrentUmask);
1142 // calculate the actual file permissions (just like open/creat)
1143 mode_t const FilePermissions = (AccessMode & ~CurrentUmask);
1144
1145 if(fchmod(iFd, FilePermissions) == -1)
1146 return FileFdErrno("fchmod", "Could not change permissions for temporary file %s", TemporaryFileName.c_str());
1147 }
1148 else
1149 iFd = open(FileName.c_str(), fileflags, AccessMode);
1150
1151 this->FileName = FileName;
1152 if (iFd == -1 || OpenInternDescriptor(Mode, compressor) == false)
1153 {
1154 if (iFd != -1)
1155 {
1156 close (iFd);
1157 iFd = -1;
1158 }
1159 return FileFdErrno("open",_("Could not open file %s"), FileName.c_str());
1160 }
1161
1162 SetCloseExec(iFd,true);
1163 return true;
1164 }
1165 /*}}}*/
1166 // FileFd::OpenDescriptor - Open a filedescriptor /*{{{*/
1167 // ---------------------------------------------------------------------
1168 /* */
1169 bool FileFd::OpenDescriptor(int Fd, unsigned int const Mode, CompressMode Compress, bool AutoClose)
1170 {
1171 std::vector<APT::Configuration::Compressor> const compressors = APT::Configuration::getCompressors();
1172 std::vector<APT::Configuration::Compressor>::const_iterator compressor = compressors.begin();
1173 std::string name;
1174
1175 // compat with the old API
1176 if (Mode == ReadOnlyGzip && Compress == None)
1177 Compress = Gzip;
1178
1179 switch (Compress)
1180 {
1181 case None: name = "."; break;
1182 case Gzip: name = "gzip"; break;
1183 case Bzip2: name = "bzip2"; break;
1184 case Lzma: name = "lzma"; break;
1185 case Xz: name = "xz"; break;
1186 case Auto:
1187 case Extension:
1188 if (AutoClose == true && Fd != -1)
1189 close(Fd);
1190 return FileFdError("Opening Fd %d in Auto or Extension compression mode is not supported", Fd);
1191 }
1192 for (; compressor != compressors.end(); ++compressor)
1193 if (compressor->Name == name)
1194 break;
1195 if (compressor == compressors.end())
1196 {
1197 if (AutoClose == true && Fd != -1)
1198 close(Fd);
1199 return FileFdError("Can't find a configured compressor %s for file %s", name.c_str(), FileName.c_str());
1200 }
1201 return OpenDescriptor(Fd, Mode, *compressor, AutoClose);
1202 }
1203 bool FileFd::OpenDescriptor(int Fd, unsigned int const Mode, APT::Configuration::Compressor const &compressor, bool AutoClose)
1204 {
1205 Close();
1206 Flags = (AutoClose) ? FileFd::AutoClose : 0;
1207 iFd = Fd;
1208 this->FileName = "";
1209 if (OpenInternDescriptor(Mode, compressor) == false)
1210 {
1211 if (iFd != -1 && (
1212 (Flags & Compressed) == Compressed ||
1213 AutoClose == true))
1214 {
1215 close (iFd);
1216 iFd = -1;
1217 }
1218 return FileFdError(_("Could not open file descriptor %d"), Fd);
1219 }
1220 return true;
1221 }
1222 bool FileFd::OpenInternDescriptor(unsigned int const Mode, APT::Configuration::Compressor const &compressor)
1223 {
1224 if (iFd == -1)
1225 return false;
1226 if (compressor.Name == "." || compressor.Binary.empty() == true)
1227 return true;
1228
1229 #if defined HAVE_ZLIB || defined HAVE_BZ2 || defined HAVE_LZMA
1230 // the API to open files is similar, so setup to avoid code duplicates later
1231 // and while at it ensure that we close before opening (if its a reopen)
1232 void* (*compress_open)(int, const char *) = NULL;
1233 if (false)
1234 /* dummy so that the rest can be 'else if's */;
1235 #define APT_COMPRESS_INIT(NAME,OPEN) \
1236 else if (compressor.Name == NAME) \
1237 { \
1238 compress_open = (void*(*)(int, const char *)) OPEN; \
1239 if (d != NULL) d->InternalClose(FileName); \
1240 }
1241 #ifdef HAVE_ZLIB
1242 APT_COMPRESS_INIT("gzip", gzdopen)
1243 #endif
1244 #ifdef HAVE_BZ2
1245 APT_COMPRESS_INIT("bzip2", BZ2_bzdopen)
1246 #endif
1247 #ifdef HAVE_LZMA
1248 APT_COMPRESS_INIT("xz", fdopen)
1249 APT_COMPRESS_INIT("lzma", fdopen)
1250 #endif
1251 #undef APT_COMPRESS_INIT
1252 #endif
1253
1254 if (d == NULL)
1255 {
1256 d = new FileFdPrivate();
1257 d->openmode = Mode;
1258 d->compressor = compressor;
1259 #if defined HAVE_ZLIB || defined HAVE_BZ2 || defined HAVE_LZMA
1260 if ((Flags & AutoClose) != AutoClose && compress_open != NULL)
1261 {
1262 // Need to duplicate fd here or gz/bz2 close for cleanup will close the fd as well
1263 int const internFd = dup(iFd);
1264 if (internFd == -1)
1265 return FileFdErrno("OpenInternDescriptor", _("Could not open file descriptor %d"), iFd);
1266 iFd = internFd;
1267 }
1268 #endif
1269 }
1270
1271 #if defined HAVE_ZLIB || defined HAVE_BZ2 || defined HAVE_LZMA
1272 if (compress_open != NULL)
1273 {
1274 void* compress_struct = NULL;
1275 if ((Mode & ReadWrite) == ReadWrite)
1276 compress_struct = compress_open(iFd, "r+");
1277 else if ((Mode & WriteOnly) == WriteOnly)
1278 compress_struct = compress_open(iFd, "w");
1279 else
1280 compress_struct = compress_open(iFd, "r");
1281 if (compress_struct == NULL)
1282 return false;
1283
1284 if (false)
1285 /* dummy so that the rest can be 'else if's */;
1286 #ifdef HAVE_ZLIB
1287 else if (compressor.Name == "gzip")
1288 d->gz = (gzFile) compress_struct;
1289 #endif
1290 #ifdef HAVE_BZ2
1291 else if (compressor.Name == "bzip2")
1292 d->bz2 = (BZFILE*) compress_struct;
1293 #endif
1294 #ifdef HAVE_LZMA
1295 else if (compressor.Name == "xz" || compressor.Name == "lzma")
1296 {
1297 uint32_t const xzlevel = 6;
1298 uint64_t const memlimit = UINT64_MAX;
1299 if (d->lzma == NULL)
1300 d->lzma = new FileFdPrivate::LZMAFILE;
1301 d->lzma->file = (FILE*) compress_struct;
1302 lzma_stream tmp_stream = LZMA_STREAM_INIT;
1303 d->lzma->stream = tmp_stream;
1304
1305 if ((Mode & ReadWrite) == ReadWrite)
1306 return FileFdError("ReadWrite mode is not supported for file %s", FileName.c_str());
1307
1308 if ((Mode & WriteOnly) == WriteOnly)
1309 {
1310 if (compressor.Name == "xz")
1311 {
1312 if (lzma_easy_encoder(&d->lzma->stream, xzlevel, LZMA_CHECK_CRC32) != LZMA_OK)
1313 return false;
1314 }
1315 else
1316 {
1317 lzma_options_lzma options;
1318 lzma_lzma_preset(&options, xzlevel);
1319 if (lzma_alone_encoder(&d->lzma->stream, &options) != LZMA_OK)
1320 return false;
1321 }
1322 d->lzma->compressing = true;
1323 }
1324 else
1325 {
1326 if (compressor.Name == "xz")
1327 {
1328 if (lzma_auto_decoder(&d->lzma->stream, memlimit, 0) != LZMA_OK)
1329 return false;
1330 }
1331 else
1332 {
1333 if (lzma_alone_decoder(&d->lzma->stream, memlimit) != LZMA_OK)
1334 return false;
1335 }
1336 d->lzma->compressing = false;
1337 }
1338 }
1339 #endif
1340 Flags |= Compressed;
1341 return true;
1342 }
1343 #endif
1344
1345 // collect zombies here in case we reopen
1346 if (d->compressor_pid > 0)
1347 ExecWait(d->compressor_pid, "FileFdCompressor", true);
1348
1349 if ((Mode & ReadWrite) == ReadWrite)
1350 return FileFdError("ReadWrite mode is not supported for file %s", FileName.c_str());
1351
1352 bool const Comp = (Mode & WriteOnly) == WriteOnly;
1353 if (Comp == false)
1354 {
1355 // Handle 'decompression' of empty files
1356 struct stat Buf;
1357 fstat(iFd, &Buf);
1358 if (Buf.st_size == 0 && S_ISFIFO(Buf.st_mode) == false)
1359 return true;
1360
1361 // We don't need the file open - instead let the compressor open it
1362 // as he properly knows better how to efficiently read from 'his' file
1363 if (FileName.empty() == false)
1364 {
1365 close(iFd);
1366 iFd = -1;
1367 }
1368 }
1369
1370 // Create a data pipe
1371 int Pipe[2] = {-1,-1};
1372 if (pipe(Pipe) != 0)
1373 return FileFdErrno("pipe",_("Failed to create subprocess IPC"));
1374 for (int J = 0; J != 2; J++)
1375 SetCloseExec(Pipe[J],true);
1376
1377 d->compressed_fd = iFd;
1378 d->pipe = true;
1379
1380 if (Comp == true)
1381 iFd = Pipe[1];
1382 else
1383 iFd = Pipe[0];
1384
1385 // The child..
1386 d->compressor_pid = ExecFork();
1387 if (d->compressor_pid == 0)
1388 {
1389 if (Comp == true)
1390 {
1391 dup2(d->compressed_fd,STDOUT_FILENO);
1392 dup2(Pipe[0],STDIN_FILENO);
1393 }
1394 else
1395 {
1396 if (d->compressed_fd != -1)
1397 dup2(d->compressed_fd,STDIN_FILENO);
1398 dup2(Pipe[1],STDOUT_FILENO);
1399 }
1400 int const nullfd = open("/dev/null", O_WRONLY);
1401 if (nullfd != -1)
1402 {
1403 dup2(nullfd,STDERR_FILENO);
1404 close(nullfd);
1405 }
1406
1407 SetCloseExec(STDOUT_FILENO,false);
1408 SetCloseExec(STDIN_FILENO,false);
1409
1410 std::vector<char const*> Args;
1411 Args.push_back(compressor.Binary.c_str());
1412 std::vector<std::string> const * const addArgs =
1413 (Comp == true) ? &(compressor.CompressArgs) : &(compressor.UncompressArgs);
1414 for (std::vector<std::string>::const_iterator a = addArgs->begin();
1415 a != addArgs->end(); ++a)
1416 Args.push_back(a->c_str());
1417 if (Comp == false && FileName.empty() == false)
1418 {
1419 // commands not needing arguments, do not need to be told about using standard output
1420 // in reality, only testcases with tools like cat, rev, rot13, … are able to trigger this
1421 if (compressor.CompressArgs.empty() == false && compressor.UncompressArgs.empty() == false)
1422 Args.push_back("--stdout");
1423 if (TemporaryFileName.empty() == false)
1424 Args.push_back(TemporaryFileName.c_str());
1425 else
1426 Args.push_back(FileName.c_str());
1427 }
1428 Args.push_back(NULL);
1429
1430 execvp(Args[0],(char **)&Args[0]);
1431 cerr << _("Failed to exec compressor ") << Args[0] << endl;
1432 _exit(100);
1433 }
1434 if (Comp == true)
1435 close(Pipe[0]);
1436 else
1437 close(Pipe[1]);
1438
1439 return true;
1440 }
1441 /*}}}*/
1442 // FileFd::~File - Closes the file /*{{{*/
1443 // ---------------------------------------------------------------------
1444 /* If the proper modes are selected then we close the Fd and possibly
1445 unlink the file on error. */
1446 FileFd::~FileFd()
1447 {
1448 Close();
1449 if (d != NULL)
1450 d->CloseDown(FileName);
1451 delete d;
1452 d = NULL;
1453 }
1454 /*}}}*/
1455 // FileFd::Read - Read a bit of the file /*{{{*/
1456 // ---------------------------------------------------------------------
1457 /* We are careful to handle interruption by a signal while reading
1458 gracefully. */
1459 bool FileFd::Read(void *To,unsigned long long Size,unsigned long long *Actual)
1460 {
1461 ssize_t Res;
1462 errno = 0;
1463 if (Actual != 0)
1464 *Actual = 0;
1465 *((char *)To) = '\0';
1466 do
1467 {
1468 if (false)
1469 /* dummy so that the rest can be 'else if's */;
1470 #ifdef HAVE_ZLIB
1471 else if (d != NULL && d->gz != NULL)
1472 Res = gzread(d->gz,To,Size);
1473 #endif
1474 #ifdef HAVE_BZ2
1475 else if (d != NULL && d->bz2 != NULL)
1476 Res = BZ2_bzread(d->bz2,To,Size);
1477 #endif
1478 #ifdef HAVE_LZMA
1479 else if (d != NULL && d->lzma != NULL)
1480 {
1481 if (d->lzma->eof == true)
1482 break;
1483
1484 d->lzma->stream.next_out = (uint8_t *) To;
1485 d->lzma->stream.avail_out = Size;
1486 if (d->lzma->stream.avail_in == 0)
1487 {
1488 d->lzma->stream.next_in = d->lzma->buffer;
1489 d->lzma->stream.avail_in = fread(d->lzma->buffer, 1, sizeof(d->lzma->buffer)/sizeof(d->lzma->buffer[0]), d->lzma->file);
1490 }
1491 d->lzma->err = lzma_code(&d->lzma->stream, LZMA_RUN);
1492 if (d->lzma->err == LZMA_STREAM_END)
1493 {
1494 d->lzma->eof = true;
1495 Res = Size - d->lzma->stream.avail_out;
1496 }
1497 else if (d->lzma->err != LZMA_OK)
1498 {
1499 Res = -1;
1500 errno = 0;
1501 }
1502 else
1503 {
1504 Res = Size - d->lzma->stream.avail_out;
1505 if (Res == 0)
1506 {
1507 // lzma run was okay, but produced no output…
1508 Res = -1;
1509 errno = EINTR;
1510 }
1511 }
1512 }
1513 #endif
1514 else
1515 Res = read(iFd,To,Size);
1516
1517 if (Res < 0)
1518 {
1519 if (errno == EINTR)
1520 {
1521 // trick the while-loop into running again
1522 Res = 1;
1523 errno = 0;
1524 continue;
1525 }
1526 if (false)
1527 /* dummy so that the rest can be 'else if's */;
1528 #ifdef HAVE_ZLIB
1529 else if (d != NULL && d->gz != NULL)
1530 {
1531 int err;
1532 char const * const errmsg = gzerror(d->gz, &err);
1533 if (err != Z_ERRNO)
1534 return FileFdError("gzread: %s (%d: %s)", _("Read error"), err, errmsg);
1535 }
1536 #endif
1537 #ifdef HAVE_BZ2
1538 else if (d != NULL && d->bz2 != NULL)
1539 {
1540 int err;
1541 char const * const errmsg = BZ2_bzerror(d->bz2, &err);
1542 if (err != BZ_IO_ERROR)
1543 return FileFdError("BZ2_bzread: %s %s (%d: %s)", FileName.c_str(), _("Read error"), err, errmsg);
1544 }
1545 #endif
1546 #ifdef HAVE_LZMA
1547 else if (d != NULL && d->lzma != NULL)
1548 return FileFdError("lzma_read: %s (%d)", _("Read error"), d->lzma->err);
1549 #endif
1550 return FileFdErrno("read",_("Read error"));
1551 }
1552
1553 To = (char *)To + Res;
1554 Size -= Res;
1555 if (d != NULL)
1556 d->seekpos += Res;
1557 if (Actual != 0)
1558 *Actual += Res;
1559 }
1560 while (Res > 0 && Size > 0);
1561
1562 if (Size == 0)
1563 return true;
1564
1565 // Eof handling
1566 if (Actual != 0)
1567 {
1568 Flags |= HitEof;
1569 return true;
1570 }
1571
1572 return FileFdError(_("read, still have %llu to read but none left"), Size);
1573 }
1574 /*}}}*/
1575 // FileFd::ReadLine - Read a complete line from the file /*{{{*/
1576 // ---------------------------------------------------------------------
1577 /* Beware: This method can be quiet slow for big buffers on UNcompressed
1578 files because of the naive implementation! */
1579 char* FileFd::ReadLine(char *To, unsigned long long const Size)
1580 {
1581 *To = '\0';
1582 #ifdef HAVE_ZLIB
1583 if (d != NULL && d->gz != NULL)
1584 return gzgets(d->gz, To, Size);
1585 #endif
1586
1587 unsigned long long read = 0;
1588 while ((Size - 1) != read)
1589 {
1590 unsigned long long done = 0;
1591 if (Read(To + read, 1, &done) == false)
1592 return NULL;
1593 if (done == 0)
1594 break;
1595 if (To[read++] == '\n')
1596 break;
1597 }
1598 if (read == 0)
1599 return NULL;
1600 To[read] = '\0';
1601 return To;
1602 }
1603 /*}}}*/
1604 // FileFd::Write - Write to the file /*{{{*/
1605 // ---------------------------------------------------------------------
1606 /* */
1607 bool FileFd::Write(const void *From,unsigned long long Size)
1608 {
1609 ssize_t Res;
1610 errno = 0;
1611 do
1612 {
1613 if (false)
1614 /* dummy so that the rest can be 'else if's */;
1615 #ifdef HAVE_ZLIB
1616 else if (d != NULL && d->gz != NULL)
1617 Res = gzwrite(d->gz,From,Size);
1618 #endif
1619 #ifdef HAVE_BZ2
1620 else if (d != NULL && d->bz2 != NULL)
1621 Res = BZ2_bzwrite(d->bz2,(void*)From,Size);
1622 #endif
1623 #ifdef HAVE_LZMA
1624 else if (d != NULL && d->lzma != NULL)
1625 {
1626 d->lzma->stream.next_in = (uint8_t *)From;
1627 d->lzma->stream.avail_in = Size;
1628 d->lzma->stream.next_out = d->lzma->buffer;
1629 d->lzma->stream.avail_out = sizeof(d->lzma->buffer)/sizeof(d->lzma->buffer[0]);
1630 d->lzma->err = lzma_code(&d->lzma->stream, LZMA_RUN);
1631 if (d->lzma->err != LZMA_OK)
1632 return false;
1633 size_t const n = sizeof(d->lzma->buffer)/sizeof(d->lzma->buffer[0]) - d->lzma->stream.avail_out;
1634 size_t const m = (n == 0) ? 0 : fwrite(d->lzma->buffer, 1, n, d->lzma->file);
1635 if (m != n)
1636 Res = -1;
1637 else
1638 Res = Size - d->lzma->stream.avail_in;
1639 }
1640 #endif
1641 else
1642 Res = write(iFd,From,Size);
1643
1644 if (Res < 0 && errno == EINTR)
1645 continue;
1646 if (Res < 0)
1647 {
1648 if (false)
1649 /* dummy so that the rest can be 'else if's */;
1650 #ifdef HAVE_ZLIB
1651 else if (d != NULL && d->gz != NULL)
1652 {
1653 int err;
1654 char const * const errmsg = gzerror(d->gz, &err);
1655 if (err != Z_ERRNO)
1656 return FileFdError("gzwrite: %s (%d: %s)", _("Write error"), err, errmsg);
1657 }
1658 #endif
1659 #ifdef HAVE_BZ2
1660 else if (d != NULL && d->bz2 != NULL)
1661 {
1662 int err;
1663 char const * const errmsg = BZ2_bzerror(d->bz2, &err);
1664 if (err != BZ_IO_ERROR)
1665 return FileFdError("BZ2_bzwrite: %s (%d: %s)", _("Write error"), err, errmsg);
1666 }
1667 #endif
1668 #ifdef HAVE_LZMA
1669 else if (d != NULL && d->lzma != NULL)
1670 return FileFdErrno("lzma_fwrite", _("Write error"));
1671 #endif
1672 return FileFdErrno("write",_("Write error"));
1673 }
1674
1675 From = (char const *)From + Res;
1676 Size -= Res;
1677 if (d != NULL)
1678 d->seekpos += Res;
1679 }
1680 while (Res > 0 && Size > 0);
1681
1682 if (Size == 0)
1683 return true;
1684
1685 return FileFdError(_("write, still have %llu to write but couldn't"), Size);
1686 }
1687 bool FileFd::Write(int Fd, const void *From, unsigned long long Size)
1688 {
1689 ssize_t Res;
1690 errno = 0;
1691 do
1692 {
1693 Res = write(Fd,From,Size);
1694 if (Res < 0 && errno == EINTR)
1695 continue;
1696 if (Res < 0)
1697 return _error->Errno("write",_("Write error"));
1698
1699 From = (char const *)From + Res;
1700 Size -= Res;
1701 }
1702 while (Res > 0 && Size > 0);
1703
1704 if (Size == 0)
1705 return true;
1706
1707 return _error->Error(_("write, still have %llu to write but couldn't"), Size);
1708 }
1709 /*}}}*/
1710 // FileFd::Seek - Seek in the file /*{{{*/
1711 // ---------------------------------------------------------------------
1712 /* */
1713 bool FileFd::Seek(unsigned long long To)
1714 {
1715 Flags &= ~HitEof;
1716
1717 if (d != NULL && (d->pipe == true || d->InternalStream() == true))
1718 {
1719 // Our poor man seeking in pipes is costly, so try to avoid it
1720 unsigned long long seekpos = Tell();
1721 if (seekpos == To)
1722 return true;
1723 else if (seekpos < To)
1724 return Skip(To - seekpos);
1725
1726 if ((d->openmode & ReadOnly) != ReadOnly)
1727 return FileFdError("Reopen is only implemented for read-only files!");
1728 d->InternalClose(FileName);
1729 if (iFd != -1)
1730 close(iFd);
1731 iFd = -1;
1732 if (TemporaryFileName.empty() == false)
1733 iFd = open(TemporaryFileName.c_str(), O_RDONLY);
1734 else if (FileName.empty() == false)
1735 iFd = open(FileName.c_str(), O_RDONLY);
1736 else
1737 {
1738 if (d->compressed_fd > 0)
1739 if (lseek(d->compressed_fd, 0, SEEK_SET) != 0)
1740 iFd = d->compressed_fd;
1741 if (iFd < 0)
1742 return FileFdError("Reopen is not implemented for pipes opened with FileFd::OpenDescriptor()!");
1743 }
1744
1745 if (OpenInternDescriptor(d->openmode, d->compressor) == false)
1746 return FileFdError("Seek on file %s because it couldn't be reopened", FileName.c_str());
1747
1748 if (To != 0)
1749 return Skip(To);
1750
1751 d->seekpos = To;
1752 return true;
1753 }
1754 off_t res;
1755 #ifdef HAVE_ZLIB
1756 if (d != NULL && d->gz)
1757 res = gzseek(d->gz,To,SEEK_SET);
1758 else
1759 #endif
1760 res = lseek(iFd,To,SEEK_SET);
1761 if (res != (off_t)To)
1762 return FileFdError("Unable to seek to %llu", To);
1763
1764 if (d != NULL)
1765 d->seekpos = To;
1766 return true;
1767 }
1768 /*}}}*/
1769 // FileFd::Skip - Seek in the file /*{{{*/
1770 // ---------------------------------------------------------------------
1771 /* */
1772 bool FileFd::Skip(unsigned long long Over)
1773 {
1774 if (d != NULL && (d->pipe == true || d->InternalStream() == true))
1775 {
1776 char buffer[1024];
1777 while (Over != 0)
1778 {
1779 unsigned long long toread = std::min((unsigned long long) sizeof(buffer), Over);
1780 if (Read(buffer, toread) == false)
1781 return FileFdError("Unable to seek ahead %llu",Over);
1782 Over -= toread;
1783 }
1784 return true;
1785 }
1786
1787 off_t res;
1788 #ifdef HAVE_ZLIB
1789 if (d != NULL && d->gz != NULL)
1790 res = gzseek(d->gz,Over,SEEK_CUR);
1791 else
1792 #endif
1793 res = lseek(iFd,Over,SEEK_CUR);
1794 if (res < 0)
1795 return FileFdError("Unable to seek ahead %llu",Over);
1796 if (d != NULL)
1797 d->seekpos = res;
1798
1799 return true;
1800 }
1801 /*}}}*/
1802 // FileFd::Truncate - Truncate the file /*{{{*/
1803 // ---------------------------------------------------------------------
1804 /* */
1805 bool FileFd::Truncate(unsigned long long To)
1806 {
1807 // truncating /dev/null is always successful - as we get an error otherwise
1808 if (To == 0 && FileName == "/dev/null")
1809 return true;
1810 #if defined HAVE_ZLIB || defined HAVE_BZ2 || defined HAVE_LZMA
1811 if (d != NULL && (d->InternalStream() == true
1812 #ifdef HAVE_ZLIB
1813 || d->gz != NULL
1814 #endif
1815 ))
1816 return FileFdError("Truncating compressed files is not implemented (%s)", FileName.c_str());
1817 #endif
1818 if (ftruncate(iFd,To) != 0)
1819 return FileFdError("Unable to truncate to %llu",To);
1820
1821 return true;
1822 }
1823 /*}}}*/
1824 // FileFd::Tell - Current seek position /*{{{*/
1825 // ---------------------------------------------------------------------
1826 /* */
1827 unsigned long long FileFd::Tell()
1828 {
1829 // In theory, we could just return seekpos here always instead of
1830 // seeking around, but not all users of FileFd use always Seek() and co
1831 // so d->seekpos isn't always true and we can just use it as a hint if
1832 // we have nothing else, but not always as an authority…
1833 if (d != NULL && (d->pipe == true || d->InternalStream() == true))
1834 return d->seekpos;
1835
1836 off_t Res;
1837 #ifdef HAVE_ZLIB
1838 if (d != NULL && d->gz != NULL)
1839 Res = gztell(d->gz);
1840 else
1841 #endif
1842 Res = lseek(iFd,0,SEEK_CUR);
1843 if (Res == (off_t)-1)
1844 FileFdErrno("lseek","Failed to determine the current file position");
1845 if (d != NULL)
1846 d->seekpos = Res;
1847 return Res;
1848 }
1849 /*}}}*/
1850 static bool StatFileFd(char const * const msg, int const iFd, std::string const &FileName, struct stat &Buf, FileFdPrivate * const d) /*{{{*/
1851 {
1852 bool ispipe = (d != NULL && d->pipe == true);
1853 if (ispipe == false)
1854 {
1855 if (fstat(iFd,&Buf) != 0)
1856 // higher-level code will generate more meaningful messages,
1857 // even translated this would be meaningless for users
1858 return _error->Errno("fstat", "Unable to determine %s for fd %i", msg, iFd);
1859 if (FileName.empty() == false)
1860 ispipe = S_ISFIFO(Buf.st_mode);
1861 }
1862
1863 // for compressor pipes st_size is undefined and at 'best' zero
1864 if (ispipe == true)
1865 {
1866 // we set it here, too, as we get the info here for free
1867 // in theory the Open-methods should take care of it already
1868 if (d != NULL)
1869 d->pipe = true;
1870 if (stat(FileName.c_str(), &Buf) != 0)
1871 return _error->Errno("fstat", "Unable to determine %s for file %s", msg, FileName.c_str());
1872 }
1873 return true;
1874 }
1875 /*}}}*/
1876 // FileFd::FileSize - Return the size of the file /*{{{*/
1877 unsigned long long FileFd::FileSize()
1878 {
1879 struct stat Buf;
1880 if (StatFileFd("file size", iFd, FileName, Buf, d) == false)
1881 {
1882 Flags |= Fail;
1883 return 0;
1884 }
1885 return Buf.st_size;
1886 }
1887 /*}}}*/
1888 // FileFd::ModificationTime - Return the time of last touch /*{{{*/
1889 time_t FileFd::ModificationTime()
1890 {
1891 struct stat Buf;
1892 if (StatFileFd("modification time", iFd, FileName, Buf, d) == false)
1893 {
1894 Flags |= Fail;
1895 return 0;
1896 }
1897 return Buf.st_mtime;
1898 }
1899 /*}}}*/
1900 // FileFd::Size - Return the size of the content in the file /*{{{*/
1901 // ---------------------------------------------------------------------
1902 /* */
1903 unsigned long long FileFd::Size()
1904 {
1905 unsigned long long size = FileSize();
1906
1907 // for compressor pipes st_size is undefined and at 'best' zero,
1908 // so we 'read' the content and 'seek' back - see there
1909 if (d != NULL && (d->pipe == true || (d->InternalStream() == true && size > 0)))
1910 {
1911 unsigned long long const oldSeek = Tell();
1912 char ignore[1000];
1913 unsigned long long read = 0;
1914 do {
1915 if (Read(ignore, sizeof(ignore), &read) == false)
1916 {
1917 Seek(oldSeek);
1918 return 0;
1919 }
1920 } while(read != 0);
1921 size = Tell();
1922 Seek(oldSeek);
1923 }
1924 #ifdef HAVE_ZLIB
1925 // only check gzsize if we are actually a gzip file, just checking for
1926 // "gz" is not sufficient as uncompressed files could be opened with
1927 // gzopen in "direct" mode as well
1928 else if (d != NULL && d->gz && !gzdirect(d->gz) && size > 0)
1929 {
1930 off_t const oldPos = lseek(iFd,0,SEEK_CUR);
1931 /* unfortunately zlib.h doesn't provide a gzsize(), so we have to do
1932 * this ourselves; the original (uncompressed) file size is the last 32
1933 * bits of the file */
1934 // FIXME: Size for gz-files is limited by 32bit… no largefile support
1935 if (lseek(iFd, -4, SEEK_END) < 0)
1936 {
1937 FileFdErrno("lseek","Unable to seek to end of gzipped file");
1938 return 0;
1939 }
1940 uint32_t size = 0;
1941 if (read(iFd, &size, 4) != 4)
1942 {
1943 FileFdErrno("read","Unable to read original size of gzipped file");
1944 return 0;
1945 }
1946 size = le32toh(size);
1947
1948 if (lseek(iFd, oldPos, SEEK_SET) < 0)
1949 {
1950 FileFdErrno("lseek","Unable to seek in gzipped file");
1951 return 0;
1952 }
1953
1954 return size;
1955 }
1956 #endif
1957
1958 return size;
1959 }
1960 /*}}}*/
1961 // FileFd::Close - Close the file if the close flag is set /*{{{*/
1962 // ---------------------------------------------------------------------
1963 /* */
1964 bool FileFd::Close()
1965 {
1966 if (iFd == -1)
1967 return true;
1968
1969 bool Res = true;
1970 if ((Flags & AutoClose) == AutoClose)
1971 {
1972 if ((Flags & Compressed) != Compressed && iFd > 0 && close(iFd) != 0)
1973 Res &= _error->Errno("close",_("Problem closing the file %s"), FileName.c_str());
1974 if (d != NULL)
1975 {
1976 Res &= d->CloseDown(FileName);
1977 delete d;
1978 d = NULL;
1979 }
1980 }
1981
1982 if ((Flags & Replace) == Replace) {
1983 if (rename(TemporaryFileName.c_str(), FileName.c_str()) != 0)
1984 Res &= _error->Errno("rename",_("Problem renaming the file %s to %s"), TemporaryFileName.c_str(), FileName.c_str());
1985
1986 FileName = TemporaryFileName; // for the unlink() below.
1987 TemporaryFileName.clear();
1988 }
1989
1990 iFd = -1;
1991
1992 if ((Flags & Fail) == Fail && (Flags & DelOnFail) == DelOnFail &&
1993 FileName.empty() == false)
1994 if (unlink(FileName.c_str()) != 0)
1995 Res &= _error->WarningE("unlnk",_("Problem unlinking the file %s"), FileName.c_str());
1996
1997 if (Res == false)
1998 Flags |= Fail;
1999 return Res;
2000 }
2001 /*}}}*/
2002 // FileFd::Sync - Sync the file /*{{{*/
2003 // ---------------------------------------------------------------------
2004 /* */
2005 bool FileFd::Sync()
2006 {
2007 if (fsync(iFd) != 0)
2008 return FileFdErrno("sync",_("Problem syncing the file"));
2009 return true;
2010 }
2011 /*}}}*/
2012 // FileFd::FileFdErrno - set Fail and call _error->Errno *{{{*/
2013 bool FileFd::FileFdErrno(const char *Function, const char *Description,...)
2014 {
2015 Flags |= Fail;
2016 va_list args;
2017 size_t msgSize = 400;
2018 int const errsv = errno;
2019 while (true)
2020 {
2021 va_start(args,Description);
2022 if (_error->InsertErrno(GlobalError::ERROR, Function, Description, args, errsv, msgSize) == false)
2023 break;
2024 va_end(args);
2025 }
2026 return false;
2027 }
2028 /*}}}*/
2029 // FileFd::FileFdError - set Fail and call _error->Error *{{{*/
2030 bool FileFd::FileFdError(const char *Description,...) {
2031 Flags |= Fail;
2032 va_list args;
2033 size_t msgSize = 400;
2034 while (true)
2035 {
2036 va_start(args,Description);
2037 if (_error->Insert(GlobalError::ERROR, Description, args, msgSize) == false)
2038 break;
2039 va_end(args);
2040 }
2041 return false;
2042 }
2043 /*}}}*/
2044
2045 APT_DEPRECATED gzFile FileFd::gzFd() {
2046 #ifdef HAVE_ZLIB
2047 return d->gz;
2048 #else
2049 return NULL;
2050 #endif
2051 }
2052
2053 // Glob - wrapper around "glob()" /*{{{*/
2054 std::vector<std::string> Glob(std::string const &pattern, int flags)
2055 {
2056 std::vector<std::string> result;
2057 glob_t globbuf;
2058 int glob_res;
2059 unsigned int i;
2060
2061 glob_res = glob(pattern.c_str(), flags, NULL, &globbuf);
2062
2063 if (glob_res != 0)
2064 {
2065 if(glob_res != GLOB_NOMATCH) {
2066 _error->Errno("glob", "Problem with glob");
2067 return result;
2068 }
2069 }
2070
2071 // append results
2072 for(i=0;i<globbuf.gl_pathc;i++)
2073 result.push_back(string(globbuf.gl_pathv[i]));
2074
2075 globfree(&globbuf);
2076 return result;
2077 }
2078 /*}}}*/
2079 std::string GetTempDir() /*{{{*/
2080 {
2081 const char *tmpdir = getenv("TMPDIR");
2082
2083 #ifdef P_tmpdir
2084 if (!tmpdir)
2085 tmpdir = P_tmpdir;
2086 #endif
2087
2088 struct stat st;
2089 if (!tmpdir || strlen(tmpdir) == 0 || // tmpdir is set
2090 stat(tmpdir, &st) != 0 || (st.st_mode & S_IFDIR) == 0 || // exists and is directory
2091 access(tmpdir, R_OK | W_OK | X_OK) != 0 // current user has rwx access to directory
2092 )
2093 tmpdir = "/tmp";
2094
2095 return string(tmpdir);
2096 }
2097 /*}}}*/
2098 FileFd* GetTempFile(std::string const &Prefix, bool ImmediateUnlink) /*{{{*/
2099 {
2100 char fn[512];
2101 FileFd *Fd = new FileFd();
2102
2103 std::string tempdir = GetTempDir();
2104 snprintf(fn, sizeof(fn), "%s/%s.XXXXXX",
2105 tempdir.c_str(), Prefix.c_str());
2106 int fd = mkstemp(fn);
2107 if(ImmediateUnlink)
2108 unlink(fn);
2109 if (fd < 0)
2110 {
2111 _error->Errno("GetTempFile",_("Unable to mkstemp %s"), fn);
2112 return NULL;
2113 }
2114 if (!Fd->OpenDescriptor(fd, FileFd::WriteOnly, FileFd::None, true))
2115 {
2116 _error->Errno("GetTempFile",_("Unable to write to %s"),fn);
2117 return NULL;
2118 }
2119
2120 return Fd;
2121 }
2122 /*}}}*/
2123 bool Rename(std::string From, std::string To) /*{{{*/
2124 {
2125 if (rename(From.c_str(),To.c_str()) != 0)
2126 {
2127 _error->Error(_("rename failed, %s (%s -> %s)."),strerror(errno),
2128 From.c_str(),To.c_str());
2129 return false;
2130 }
2131 return true;
2132 }
2133 /*}}}*/
2134 bool Popen(const char* Args[], FileFd &Fd, pid_t &Child, FileFd::OpenMode Mode)/*{{{*/
2135 {
2136 int fd;
2137 if (Mode != FileFd::ReadOnly && Mode != FileFd::WriteOnly)
2138 return _error->Error("Popen supports ReadOnly (x)or WriteOnly mode only");
2139
2140 int Pipe[2] = {-1, -1};
2141 if(pipe(Pipe) != 0)
2142 return _error->Errno("pipe", _("Failed to create subprocess IPC"));
2143
2144 std::set<int> keep_fds;
2145 keep_fds.insert(Pipe[0]);
2146 keep_fds.insert(Pipe[1]);
2147 Child = ExecFork(keep_fds);
2148 if(Child < 0)
2149 return _error->Errno("fork", "Failed to fork");
2150 if(Child == 0)
2151 {
2152 if(Mode == FileFd::ReadOnly)
2153 {
2154 close(Pipe[0]);
2155 fd = Pipe[1];
2156 }
2157 else if(Mode == FileFd::WriteOnly)
2158 {
2159 close(Pipe[1]);
2160 fd = Pipe[0];
2161 }
2162
2163 if(Mode == FileFd::ReadOnly)
2164 {
2165 dup2(fd, 1);
2166 dup2(fd, 2);
2167 } else if(Mode == FileFd::WriteOnly)
2168 dup2(fd, 0);
2169
2170 execv(Args[0], (char**)Args);
2171 _exit(100);
2172 }
2173 if(Mode == FileFd::ReadOnly)
2174 {
2175 close(Pipe[1]);
2176 fd = Pipe[0];
2177 } else if(Mode == FileFd::WriteOnly)
2178 {
2179 close(Pipe[0]);
2180 fd = Pipe[1];
2181 }
2182 Fd.OpenDescriptor(fd, Mode, FileFd::None, true);
2183
2184 return true;
2185 }
2186 /*}}}*/
2187 bool DropPrivileges() /*{{{*/
2188 {
2189 if(_config->FindB("Debug::NoDropPrivs", false) == true)
2190 return true;
2191
2192 #if __gnu_linux__
2193 #if defined(PR_SET_NO_NEW_PRIVS) && ( PR_SET_NO_NEW_PRIVS != 38 )
2194 #error "PR_SET_NO_NEW_PRIVS is defined, but with a different value than expected!"
2195 #endif
2196 // see prctl(2), needs linux3.5 at runtime - magic constant to avoid it at buildtime
2197 int ret = prctl(38, 1, 0, 0, 0);
2198 // ignore EINVAL - kernel is too old to understand the option
2199 if(ret < 0 && errno != EINVAL)
2200 _error->Warning("PR_SET_NO_NEW_PRIVS failed with %i", ret);
2201 #endif
2202
2203 // empty setting disables privilege dropping - this also ensures
2204 // backward compatibility, see bug #764506
2205 const std::string toUser = _config->Find("APT::Sandbox::User");
2206 if (toUser.empty())
2207 return true;
2208
2209 // uid will be 0 in the end, but gid might be different anyway
2210 uid_t const old_uid = getuid();
2211 gid_t const old_gid = getgid();
2212
2213 if (old_uid != 0)
2214 return true;
2215
2216 struct passwd *pw = getpwnam(toUser.c_str());
2217 if (pw == NULL)
2218 return _error->Error("No user %s, can not drop rights", toUser.c_str());
2219
2220 // Do not change the order here, it might break things
2221 if (setgroups(1, &pw->pw_gid))
2222 return _error->Errno("setgroups", "Failed to setgroups");
2223
2224 if (setegid(pw->pw_gid) != 0)
2225 return _error->Errno("setegid", "Failed to setegid");
2226
2227 if (setgid(pw->pw_gid) != 0)
2228 return _error->Errno("setgid", "Failed to setgid");
2229
2230 if (setuid(pw->pw_uid) != 0)
2231 return _error->Errno("setuid", "Failed to setuid");
2232
2233 // the seteuid() is probably uneeded (at least thats what the linux
2234 // man-page says about setuid(2)) but we cargo culted it anyway
2235 if (seteuid(pw->pw_uid) != 0)
2236 return _error->Errno("seteuid", "Failed to seteuid");
2237
2238 // Verify that the user has only a single group, and the correct one
2239 gid_t groups[1];
2240 if (getgroups(1, groups) != 1)
2241 return _error->Errno("getgroups", "Could not get new groups");
2242 if (groups[0] != pw->pw_gid)
2243 return _error->Error("Could not switch group");
2244
2245 // Verify that gid, egid, uid, and euid changed
2246 if (getgid() != pw->pw_gid)
2247 return _error->Error("Could not switch group");
2248 if (getegid() != pw->pw_gid)
2249 return _error->Error("Could not switch effective group");
2250 if (getuid() != pw->pw_uid)
2251 return _error->Error("Could not switch user");
2252 if (geteuid() != pw->pw_uid)
2253 return _error->Error("Could not switch effective user");
2254
2255 #ifdef HAVE_GETRESUID
2256 // verify that the saved set-user-id was changed as well
2257 uid_t ruid = 0;
2258 uid_t euid = 0;
2259 uid_t suid = 0;
2260 if (getresuid(&ruid, &euid, &suid))
2261 return _error->Errno("getresuid", "Could not get saved set-user-ID");
2262 if (suid != pw->pw_uid)
2263 return _error->Error("Could not switch saved set-user-ID");
2264 #endif
2265
2266 #ifdef HAVE_GETRESGID
2267 // verify that the saved set-group-id was changed as well
2268 gid_t rgid = 0;
2269 gid_t egid = 0;
2270 gid_t sgid = 0;
2271 if (getresgid(&rgid, &egid, &sgid))
2272 return _error->Errno("getresuid", "Could not get saved set-group-ID");
2273 if (sgid != pw->pw_gid)
2274 return _error->Error("Could not switch saved set-group-ID");
2275 #endif
2276
2277 // Check that uid and gid changes do not work anymore
2278 if (pw->pw_gid != old_gid && (setgid(old_gid) != -1 || setegid(old_gid) != -1))
2279 return _error->Error("Could restore a gid to root, privilege dropping did not work");
2280
2281 if (pw->pw_uid != old_uid && (setuid(old_uid) != -1 || seteuid(old_uid) != -1))
2282 return _error->Error("Could restore a uid to root, privilege dropping did not work");
2283
2284 return true;
2285 }
2286 /*}}}*/