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