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