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