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