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