]> git.saurik.com Git - apt.git/blob - apt-pkg/contrib/fileutl.cc
add volatile sources support in libapt-pkg
[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 long ScOpenMax = sysconf(_SC_OPEN_MAX);
804 // Close all of our FDs - just in case
805 for (int K = 3; K != ScOpenMax; K++)
806 {
807 if(KeepFDs.find(K) == KeepFDs.end())
808 fcntl(K,F_SETFD,FD_CLOEXEC);
809 }
810 }
811
812 return Process;
813 }
814 /*}}}*/
815 // ExecWait - Fancy waitpid /*{{{*/
816 // ---------------------------------------------------------------------
817 /* Waits for the given sub process. If Reap is set then no errors are
818 generated. Otherwise a failed subprocess will generate a proper descriptive
819 message */
820 bool ExecWait(pid_t Pid,const char *Name,bool Reap)
821 {
822 if (Pid <= 1)
823 return true;
824
825 // Wait and collect the error code
826 int Status;
827 while (waitpid(Pid,&Status,0) != Pid)
828 {
829 if (errno == EINTR)
830 continue;
831
832 if (Reap == true)
833 return false;
834
835 return _error->Error(_("Waited for %s but it wasn't there"),Name);
836 }
837
838
839 // Check for an error code.
840 if (WIFEXITED(Status) == 0 || WEXITSTATUS(Status) != 0)
841 {
842 if (Reap == true)
843 return false;
844 if (WIFSIGNALED(Status) != 0)
845 {
846 if( WTERMSIG(Status) == SIGSEGV)
847 return _error->Error(_("Sub-process %s received a segmentation fault."),Name);
848 else
849 return _error->Error(_("Sub-process %s received signal %u."),Name, WTERMSIG(Status));
850 }
851
852 if (WIFEXITED(Status) != 0)
853 return _error->Error(_("Sub-process %s returned an error code (%u)"),Name,WEXITSTATUS(Status));
854
855 return _error->Error(_("Sub-process %s exited unexpectedly"),Name);
856 }
857
858 return true;
859 }
860 /*}}}*/
861 // StartsWithGPGClearTextSignature - Check if a file is Pgp/GPG clearsigned /*{{{*/
862 bool StartsWithGPGClearTextSignature(string const &FileName)
863 {
864 static const char* SIGMSG = "-----BEGIN PGP SIGNED MESSAGE-----\n";
865 char buffer[strlen(SIGMSG)+1];
866 FILE* gpg = fopen(FileName.c_str(), "r");
867 if (gpg == NULL)
868 return false;
869
870 char const * const test = fgets(buffer, sizeof(buffer), gpg);
871 fclose(gpg);
872 if (test == NULL || strcmp(buffer, SIGMSG) != 0)
873 return false;
874
875 return true;
876 }
877 /*}}}*/
878 // ChangeOwnerAndPermissionOfFile - set file attributes to requested values /*{{{*/
879 bool ChangeOwnerAndPermissionOfFile(char const * const requester, char const * const file, char const * const user, char const * const group, mode_t const mode)
880 {
881 if (strcmp(file, "/dev/null") == 0)
882 return true;
883 bool Res = true;
884 if (getuid() == 0 && strlen(user) != 0 && strlen(group) != 0) // if we aren't root, we can't chown, so don't try it
885 {
886 // ensure the file is owned by root and has good permissions
887 struct passwd const * const pw = getpwnam(user);
888 struct group const * const gr = getgrnam(group);
889 if (pw != NULL && gr != NULL && chown(file, pw->pw_uid, gr->gr_gid) != 0)
890 Res &= _error->WarningE(requester, "chown to %s:%s of file %s failed", user, group, file);
891 }
892 if (chmod(file, mode) != 0)
893 Res &= _error->WarningE(requester, "chmod 0%o of file %s failed", mode, file);
894 return Res;
895 }
896 /*}}}*/
897
898 class FileFdPrivate { /*{{{*/
899 public:
900 #ifdef HAVE_ZLIB
901 gzFile gz;
902 #endif
903 #ifdef HAVE_BZ2
904 BZFILE* bz2;
905 #endif
906 #ifdef HAVE_LZMA
907 struct LZMAFILE {
908 FILE* file;
909 uint8_t buffer[4096];
910 lzma_stream stream;
911 lzma_ret err;
912 bool eof;
913 bool compressing;
914
915 LZMAFILE() : file(NULL), eof(false), compressing(false) { buffer[0] = '\0'; }
916 ~LZMAFILE() {
917 if (compressing == true)
918 {
919 for (;;) {
920 stream.avail_out = sizeof(buffer)/sizeof(buffer[0]);
921 stream.next_out = buffer;
922 err = lzma_code(&stream, LZMA_FINISH);
923 if (err != LZMA_OK && err != LZMA_STREAM_END)
924 {
925 _error->Error("~LZMAFILE: Compress finalisation failed");
926 break;
927 }
928 size_t const n = sizeof(buffer)/sizeof(buffer[0]) - stream.avail_out;
929 if (n && fwrite(buffer, 1, n, file) != n)
930 {
931 _error->Errno("~LZMAFILE",_("Write error"));
932 break;
933 }
934 if (err == LZMA_STREAM_END)
935 break;
936 }
937 }
938 lzma_end(&stream);
939 fclose(file);
940 }
941 };
942 LZMAFILE* lzma;
943 #endif
944 int compressed_fd;
945 pid_t compressor_pid;
946 bool pipe;
947 APT::Configuration::Compressor compressor;
948 unsigned int openmode;
949 unsigned long long seekpos;
950 FileFdPrivate() :
951 #ifdef HAVE_ZLIB
952 gz(NULL),
953 #endif
954 #ifdef HAVE_BZ2
955 bz2(NULL),
956 #endif
957 #ifdef HAVE_LZMA
958 lzma(NULL),
959 #endif
960 compressed_fd(-1), compressor_pid(-1), pipe(false),
961 openmode(0), seekpos(0) {};
962 bool InternalClose(std::string const &FileName)
963 {
964 if (false)
965 /* dummy so that the rest can be 'else if's */;
966 #ifdef HAVE_ZLIB
967 else if (gz != NULL) {
968 int const e = gzclose(gz);
969 gz = NULL;
970 // gzdclose() on empty files always fails with "buffer error" here, ignore that
971 if (e != 0 && e != Z_BUF_ERROR)
972 return _error->Errno("close",_("Problem closing the gzip file %s"), FileName.c_str());
973 }
974 #endif
975 #ifdef HAVE_BZ2
976 else if (bz2 != NULL) {
977 BZ2_bzclose(bz2);
978 bz2 = NULL;
979 }
980 #endif
981 #ifdef HAVE_LZMA
982 else if (lzma != NULL) {
983 delete lzma;
984 lzma = NULL;
985 }
986 #endif
987 return true;
988 }
989 bool CloseDown(std::string const &FileName)
990 {
991 bool const Res = InternalClose(FileName);
992
993 if (compressor_pid > 0)
994 ExecWait(compressor_pid, "FileFdCompressor", true);
995 compressor_pid = -1;
996
997 return Res;
998 }
999 bool InternalStream() const {
1000 return false
1001 #ifdef HAVE_BZ2
1002 || bz2 != NULL
1003 #endif
1004 #ifdef HAVE_LZMA
1005 || lzma != NULL
1006 #endif
1007 ;
1008 }
1009
1010
1011 ~FileFdPrivate() { CloseDown(""); }
1012 };
1013 /*}}}*/
1014 // FileFd Constructors /*{{{*/
1015 FileFd::FileFd(std::string FileName,unsigned int const Mode,unsigned long AccessMode) : iFd(-1), Flags(0), d(NULL)
1016 {
1017 Open(FileName,Mode, None, AccessMode);
1018 }
1019 FileFd::FileFd(std::string FileName,unsigned int const Mode, CompressMode Compress, unsigned long AccessMode) : iFd(-1), Flags(0), d(NULL)
1020 {
1021 Open(FileName,Mode, Compress, AccessMode);
1022 }
1023 FileFd::FileFd() : iFd(-1), Flags(AutoClose), d(NULL) {}
1024 FileFd::FileFd(int const Fd, unsigned int const Mode, CompressMode Compress) : iFd(-1), Flags(0), d(NULL)
1025 {
1026 OpenDescriptor(Fd, Mode, Compress);
1027 }
1028 FileFd::FileFd(int const Fd, bool const AutoClose) : iFd(-1), Flags(0), d(NULL)
1029 {
1030 OpenDescriptor(Fd, ReadWrite, None, AutoClose);
1031 }
1032 /*}}}*/
1033 // FileFd::Open - Open a file /*{{{*/
1034 // ---------------------------------------------------------------------
1035 /* The most commonly used open mode combinations are given with Mode */
1036 bool FileFd::Open(string FileName,unsigned int const Mode,CompressMode Compress, unsigned long const AccessMode)
1037 {
1038 if (Mode == ReadOnlyGzip)
1039 return Open(FileName, ReadOnly, Gzip, AccessMode);
1040
1041 if (Compress == Auto && (Mode & WriteOnly) == WriteOnly)
1042 return FileFdError("Autodetection on %s only works in ReadOnly openmode!", FileName.c_str());
1043
1044 std::vector<APT::Configuration::Compressor> const compressors = APT::Configuration::getCompressors();
1045 std::vector<APT::Configuration::Compressor>::const_iterator compressor = compressors.begin();
1046 if (Compress == Auto)
1047 {
1048 for (; compressor != compressors.end(); ++compressor)
1049 {
1050 std::string file = FileName + compressor->Extension;
1051 if (FileExists(file) == false)
1052 continue;
1053 FileName = file;
1054 break;
1055 }
1056 }
1057 else if (Compress == Extension)
1058 {
1059 std::string::size_type const found = FileName.find_last_of('.');
1060 std::string ext;
1061 if (found != std::string::npos)
1062 {
1063 ext = FileName.substr(found);
1064 if (ext == ".new" || ext == ".bak")
1065 {
1066 std::string::size_type const found2 = FileName.find_last_of('.', found - 1);
1067 if (found2 != std::string::npos)
1068 ext = FileName.substr(found2, found - found2);
1069 else
1070 ext.clear();
1071 }
1072 }
1073 for (; compressor != compressors.end(); ++compressor)
1074 if (ext == compressor->Extension)
1075 break;
1076 // no matching extension - assume uncompressed (imagine files like 'example.org_Packages')
1077 if (compressor == compressors.end())
1078 for (compressor = compressors.begin(); compressor != compressors.end(); ++compressor)
1079 if (compressor->Name == ".")
1080 break;
1081 }
1082 else
1083 {
1084 std::string name;
1085 switch (Compress)
1086 {
1087 case None: name = "."; break;
1088 case Gzip: name = "gzip"; break;
1089 case Bzip2: name = "bzip2"; break;
1090 case Lzma: name = "lzma"; break;
1091 case Xz: name = "xz"; break;
1092 case Auto:
1093 case Extension:
1094 // Unreachable
1095 return FileFdError("Opening File %s in None, Auto or Extension should be already handled?!?", FileName.c_str());
1096 }
1097 for (; compressor != compressors.end(); ++compressor)
1098 if (compressor->Name == name)
1099 break;
1100 if (compressor == compressors.end())
1101 return FileFdError("Can't find a configured compressor %s for file %s", name.c_str(), FileName.c_str());
1102 }
1103
1104 if (compressor == compressors.end())
1105 return FileFdError("Can't find a match for specified compressor mode for file %s", FileName.c_str());
1106 return Open(FileName, Mode, *compressor, AccessMode);
1107 }
1108 bool FileFd::Open(string FileName,unsigned int const Mode,APT::Configuration::Compressor const &compressor, unsigned long const AccessMode)
1109 {
1110 Close();
1111 Flags = AutoClose;
1112
1113 if ((Mode & WriteOnly) != WriteOnly && (Mode & (Atomic | Create | Empty | Exclusive)) != 0)
1114 return FileFdError("ReadOnly mode for %s doesn't accept additional flags!", FileName.c_str());
1115 if ((Mode & ReadWrite) == 0)
1116 return FileFdError("No openmode provided in FileFd::Open for %s", FileName.c_str());
1117
1118 if ((Mode & Atomic) == Atomic)
1119 {
1120 Flags |= Replace;
1121 }
1122 else if ((Mode & (Exclusive | Create)) == (Exclusive | Create))
1123 {
1124 // for atomic, this will be done by rename in Close()
1125 unlink(FileName.c_str());
1126 }
1127 if ((Mode & Empty) == Empty)
1128 {
1129 struct stat Buf;
1130 if (lstat(FileName.c_str(),&Buf) == 0 && S_ISLNK(Buf.st_mode))
1131 unlink(FileName.c_str());
1132 }
1133
1134 int fileflags = 0;
1135 #define if_FLAGGED_SET(FLAG, MODE) if ((Mode & FLAG) == FLAG) fileflags |= MODE
1136 if_FLAGGED_SET(ReadWrite, O_RDWR);
1137 else if_FLAGGED_SET(ReadOnly, O_RDONLY);
1138 else if_FLAGGED_SET(WriteOnly, O_WRONLY);
1139
1140 if_FLAGGED_SET(Create, O_CREAT);
1141 if_FLAGGED_SET(Empty, O_TRUNC);
1142 if_FLAGGED_SET(Exclusive, O_EXCL);
1143 #undef if_FLAGGED_SET
1144
1145 if ((Mode & Atomic) == Atomic)
1146 {
1147 char *name = strdup((FileName + ".XXXXXX").c_str());
1148
1149 if((iFd = mkstemp(name)) == -1)
1150 {
1151 free(name);
1152 return FileFdErrno("mkstemp", "Could not create temporary file for %s", FileName.c_str());
1153 }
1154
1155 TemporaryFileName = string(name);
1156 free(name);
1157
1158 // umask() will always set the umask and return the previous value, so
1159 // we first set the umask and then reset it to the old value
1160 mode_t const CurrentUmask = umask(0);
1161 umask(CurrentUmask);
1162 // calculate the actual file permissions (just like open/creat)
1163 mode_t const FilePermissions = (AccessMode & ~CurrentUmask);
1164
1165 if(fchmod(iFd, FilePermissions) == -1)
1166 return FileFdErrno("fchmod", "Could not change permissions for temporary file %s", TemporaryFileName.c_str());
1167 }
1168 else
1169 iFd = open(FileName.c_str(), fileflags, AccessMode);
1170
1171 this->FileName = FileName;
1172 if (iFd == -1 || OpenInternDescriptor(Mode, compressor) == false)
1173 {
1174 if (iFd != -1)
1175 {
1176 close (iFd);
1177 iFd = -1;
1178 }
1179 return FileFdErrno("open",_("Could not open file %s"), FileName.c_str());
1180 }
1181
1182 SetCloseExec(iFd,true);
1183 return true;
1184 }
1185 /*}}}*/
1186 // FileFd::OpenDescriptor - Open a filedescriptor /*{{{*/
1187 // ---------------------------------------------------------------------
1188 /* */
1189 bool FileFd::OpenDescriptor(int Fd, unsigned int const Mode, CompressMode Compress, bool AutoClose)
1190 {
1191 std::vector<APT::Configuration::Compressor> const compressors = APT::Configuration::getCompressors();
1192 std::vector<APT::Configuration::Compressor>::const_iterator compressor = compressors.begin();
1193 std::string name;
1194
1195 // compat with the old API
1196 if (Mode == ReadOnlyGzip && Compress == None)
1197 Compress = Gzip;
1198
1199 switch (Compress)
1200 {
1201 case None: name = "."; break;
1202 case Gzip: name = "gzip"; break;
1203 case Bzip2: name = "bzip2"; break;
1204 case Lzma: name = "lzma"; break;
1205 case Xz: name = "xz"; break;
1206 case Auto:
1207 case Extension:
1208 if (AutoClose == true && Fd != -1)
1209 close(Fd);
1210 return FileFdError("Opening Fd %d in Auto or Extension compression mode is not supported", Fd);
1211 }
1212 for (; compressor != compressors.end(); ++compressor)
1213 if (compressor->Name == name)
1214 break;
1215 if (compressor == compressors.end())
1216 {
1217 if (AutoClose == true && Fd != -1)
1218 close(Fd);
1219 return FileFdError("Can't find a configured compressor %s for file %s", name.c_str(), FileName.c_str());
1220 }
1221 return OpenDescriptor(Fd, Mode, *compressor, AutoClose);
1222 }
1223 bool FileFd::OpenDescriptor(int Fd, unsigned int const Mode, APT::Configuration::Compressor const &compressor, bool AutoClose)
1224 {
1225 Close();
1226 Flags = (AutoClose) ? FileFd::AutoClose : 0;
1227 iFd = Fd;
1228 this->FileName = "";
1229 if (OpenInternDescriptor(Mode, compressor) == false)
1230 {
1231 if (iFd != -1 && (
1232 (Flags & Compressed) == Compressed ||
1233 AutoClose == true))
1234 {
1235 close (iFd);
1236 iFd = -1;
1237 }
1238 return FileFdError(_("Could not open file descriptor %d"), Fd);
1239 }
1240 return true;
1241 }
1242 bool FileFd::OpenInternDescriptor(unsigned int const Mode, APT::Configuration::Compressor const &compressor)
1243 {
1244 if (iFd == -1)
1245 return false;
1246 if (compressor.Name == "." || compressor.Binary.empty() == true)
1247 return true;
1248
1249 #if defined HAVE_ZLIB || defined HAVE_BZ2 || defined HAVE_LZMA
1250 // the API to open files is similar, so setup to avoid code duplicates later
1251 // and while at it ensure that we close before opening (if its a reopen)
1252 void* (*compress_open)(int, const char *) = NULL;
1253 if (false)
1254 /* dummy so that the rest can be 'else if's */;
1255 #define APT_COMPRESS_INIT(NAME,OPEN) \
1256 else if (compressor.Name == NAME) \
1257 { \
1258 compress_open = (void*(*)(int, const char *)) OPEN; \
1259 if (d != NULL) d->InternalClose(FileName); \
1260 }
1261 #ifdef HAVE_ZLIB
1262 APT_COMPRESS_INIT("gzip", gzdopen)
1263 #endif
1264 #ifdef HAVE_BZ2
1265 APT_COMPRESS_INIT("bzip2", BZ2_bzdopen)
1266 #endif
1267 #ifdef HAVE_LZMA
1268 APT_COMPRESS_INIT("xz", fdopen)
1269 APT_COMPRESS_INIT("lzma", fdopen)
1270 #endif
1271 #undef APT_COMPRESS_INIT
1272 #endif
1273
1274 if (d == NULL)
1275 {
1276 d = new FileFdPrivate();
1277 d->openmode = Mode;
1278 d->compressor = compressor;
1279 #if defined HAVE_ZLIB || defined HAVE_BZ2 || defined HAVE_LZMA
1280 if ((Flags & AutoClose) != AutoClose && compress_open != NULL)
1281 {
1282 // Need to duplicate fd here or gz/bz2 close for cleanup will close the fd as well
1283 int const internFd = dup(iFd);
1284 if (internFd == -1)
1285 return FileFdErrno("OpenInternDescriptor", _("Could not open file descriptor %d"), iFd);
1286 iFd = internFd;
1287 }
1288 #endif
1289 }
1290
1291 #if defined HAVE_ZLIB || defined HAVE_BZ2 || defined HAVE_LZMA
1292 if (compress_open != NULL)
1293 {
1294 void* compress_struct = NULL;
1295 if ((Mode & ReadWrite) == ReadWrite)
1296 compress_struct = compress_open(iFd, "r+");
1297 else if ((Mode & WriteOnly) == WriteOnly)
1298 compress_struct = compress_open(iFd, "w");
1299 else
1300 compress_struct = compress_open(iFd, "r");
1301 if (compress_struct == NULL)
1302 return false;
1303
1304 if (false)
1305 /* dummy so that the rest can be 'else if's */;
1306 #ifdef HAVE_ZLIB
1307 else if (compressor.Name == "gzip")
1308 d->gz = (gzFile) compress_struct;
1309 #endif
1310 #ifdef HAVE_BZ2
1311 else if (compressor.Name == "bzip2")
1312 d->bz2 = (BZFILE*) compress_struct;
1313 #endif
1314 #ifdef HAVE_LZMA
1315 else if (compressor.Name == "xz" || compressor.Name == "lzma")
1316 {
1317 uint32_t const xzlevel = 6;
1318 uint64_t const memlimit = UINT64_MAX;
1319 if (d->lzma == NULL)
1320 d->lzma = new FileFdPrivate::LZMAFILE;
1321 d->lzma->file = (FILE*) compress_struct;
1322 lzma_stream tmp_stream = LZMA_STREAM_INIT;
1323 d->lzma->stream = tmp_stream;
1324
1325 if ((Mode & ReadWrite) == ReadWrite)
1326 return FileFdError("ReadWrite mode is not supported for file %s", FileName.c_str());
1327
1328 if ((Mode & WriteOnly) == WriteOnly)
1329 {
1330 if (compressor.Name == "xz")
1331 {
1332 if (lzma_easy_encoder(&d->lzma->stream, xzlevel, LZMA_CHECK_CRC32) != LZMA_OK)
1333 return false;
1334 }
1335 else
1336 {
1337 lzma_options_lzma options;
1338 lzma_lzma_preset(&options, xzlevel);
1339 if (lzma_alone_encoder(&d->lzma->stream, &options) != LZMA_OK)
1340 return false;
1341 }
1342 d->lzma->compressing = true;
1343 }
1344 else
1345 {
1346 if (compressor.Name == "xz")
1347 {
1348 if (lzma_auto_decoder(&d->lzma->stream, memlimit, 0) != LZMA_OK)
1349 return false;
1350 }
1351 else
1352 {
1353 if (lzma_alone_decoder(&d->lzma->stream, memlimit) != LZMA_OK)
1354 return false;
1355 }
1356 d->lzma->compressing = false;
1357 }
1358 }
1359 #endif
1360 Flags |= Compressed;
1361 return true;
1362 }
1363 #endif
1364
1365 // collect zombies here in case we reopen
1366 if (d->compressor_pid > 0)
1367 ExecWait(d->compressor_pid, "FileFdCompressor", true);
1368
1369 if ((Mode & ReadWrite) == ReadWrite)
1370 return FileFdError("ReadWrite mode is not supported for file %s", FileName.c_str());
1371
1372 bool const Comp = (Mode & WriteOnly) == WriteOnly;
1373 if (Comp == false)
1374 {
1375 // Handle 'decompression' of empty files
1376 struct stat Buf;
1377 fstat(iFd, &Buf);
1378 if (Buf.st_size == 0 && S_ISFIFO(Buf.st_mode) == false)
1379 return true;
1380
1381 // We don't need the file open - instead let the compressor open it
1382 // as he properly knows better how to efficiently read from 'his' file
1383 if (FileName.empty() == false)
1384 {
1385 close(iFd);
1386 iFd = -1;
1387 }
1388 }
1389
1390 // Create a data pipe
1391 int Pipe[2] = {-1,-1};
1392 if (pipe(Pipe) != 0)
1393 return FileFdErrno("pipe",_("Failed to create subprocess IPC"));
1394 for (int J = 0; J != 2; J++)
1395 SetCloseExec(Pipe[J],true);
1396
1397 d->compressed_fd = iFd;
1398 d->pipe = true;
1399
1400 if (Comp == true)
1401 iFd = Pipe[1];
1402 else
1403 iFd = Pipe[0];
1404
1405 // The child..
1406 d->compressor_pid = ExecFork();
1407 if (d->compressor_pid == 0)
1408 {
1409 if (Comp == true)
1410 {
1411 dup2(d->compressed_fd,STDOUT_FILENO);
1412 dup2(Pipe[0],STDIN_FILENO);
1413 }
1414 else
1415 {
1416 if (d->compressed_fd != -1)
1417 dup2(d->compressed_fd,STDIN_FILENO);
1418 dup2(Pipe[1],STDOUT_FILENO);
1419 }
1420 int const nullfd = open("/dev/null", O_WRONLY);
1421 if (nullfd != -1)
1422 {
1423 dup2(nullfd,STDERR_FILENO);
1424 close(nullfd);
1425 }
1426
1427 SetCloseExec(STDOUT_FILENO,false);
1428 SetCloseExec(STDIN_FILENO,false);
1429
1430 std::vector<char const*> Args;
1431 Args.push_back(compressor.Binary.c_str());
1432 std::vector<std::string> const * const addArgs =
1433 (Comp == true) ? &(compressor.CompressArgs) : &(compressor.UncompressArgs);
1434 for (std::vector<std::string>::const_iterator a = addArgs->begin();
1435 a != addArgs->end(); ++a)
1436 Args.push_back(a->c_str());
1437 if (Comp == false && FileName.empty() == false)
1438 {
1439 // commands not needing arguments, do not need to be told about using standard output
1440 // in reality, only testcases with tools like cat, rev, rot13, … are able to trigger this
1441 if (compressor.CompressArgs.empty() == false && compressor.UncompressArgs.empty() == false)
1442 Args.push_back("--stdout");
1443 if (TemporaryFileName.empty() == false)
1444 Args.push_back(TemporaryFileName.c_str());
1445 else
1446 Args.push_back(FileName.c_str());
1447 }
1448 Args.push_back(NULL);
1449
1450 execvp(Args[0],(char **)&Args[0]);
1451 cerr << _("Failed to exec compressor ") << Args[0] << endl;
1452 _exit(100);
1453 }
1454 if (Comp == true)
1455 close(Pipe[0]);
1456 else
1457 close(Pipe[1]);
1458
1459 return true;
1460 }
1461 /*}}}*/
1462 // FileFd::~File - Closes the file /*{{{*/
1463 // ---------------------------------------------------------------------
1464 /* If the proper modes are selected then we close the Fd and possibly
1465 unlink the file on error. */
1466 FileFd::~FileFd()
1467 {
1468 Close();
1469 if (d != NULL)
1470 d->CloseDown(FileName);
1471 delete d;
1472 d = NULL;
1473 }
1474 /*}}}*/
1475 // FileFd::Read - Read a bit of the file /*{{{*/
1476 // ---------------------------------------------------------------------
1477 /* We are careful to handle interruption by a signal while reading
1478 gracefully. */
1479 bool FileFd::Read(void *To,unsigned long long Size,unsigned long long *Actual)
1480 {
1481 ssize_t Res;
1482 errno = 0;
1483 if (Actual != 0)
1484 *Actual = 0;
1485 *((char *)To) = '\0';
1486 do
1487 {
1488 if (false)
1489 /* dummy so that the rest can be 'else if's */;
1490 #ifdef HAVE_ZLIB
1491 else if (d != NULL && d->gz != NULL)
1492 Res = gzread(d->gz,To,Size);
1493 #endif
1494 #ifdef HAVE_BZ2
1495 else if (d != NULL && d->bz2 != NULL)
1496 Res = BZ2_bzread(d->bz2,To,Size);
1497 #endif
1498 #ifdef HAVE_LZMA
1499 else if (d != NULL && d->lzma != NULL)
1500 {
1501 if (d->lzma->eof == true)
1502 break;
1503
1504 d->lzma->stream.next_out = (uint8_t *) To;
1505 d->lzma->stream.avail_out = Size;
1506 if (d->lzma->stream.avail_in == 0)
1507 {
1508 d->lzma->stream.next_in = d->lzma->buffer;
1509 d->lzma->stream.avail_in = fread(d->lzma->buffer, 1, sizeof(d->lzma->buffer)/sizeof(d->lzma->buffer[0]), d->lzma->file);
1510 }
1511 d->lzma->err = lzma_code(&d->lzma->stream, LZMA_RUN);
1512 if (d->lzma->err == LZMA_STREAM_END)
1513 {
1514 d->lzma->eof = true;
1515 Res = Size - d->lzma->stream.avail_out;
1516 }
1517 else if (d->lzma->err != LZMA_OK)
1518 {
1519 Res = -1;
1520 errno = 0;
1521 }
1522 else
1523 {
1524 Res = Size - d->lzma->stream.avail_out;
1525 if (Res == 0)
1526 {
1527 // lzma run was okay, but produced no output…
1528 Res = -1;
1529 errno = EINTR;
1530 }
1531 }
1532 }
1533 #endif
1534 else
1535 Res = read(iFd,To,Size);
1536
1537 if (Res < 0)
1538 {
1539 if (errno == EINTR)
1540 {
1541 // trick the while-loop into running again
1542 Res = 1;
1543 errno = 0;
1544 continue;
1545 }
1546 if (false)
1547 /* dummy so that the rest can be 'else if's */;
1548 #ifdef HAVE_ZLIB
1549 else if (d != NULL && d->gz != NULL)
1550 {
1551 int err;
1552 char const * const errmsg = gzerror(d->gz, &err);
1553 if (err != Z_ERRNO)
1554 return FileFdError("gzread: %s (%d: %s)", _("Read error"), err, errmsg);
1555 }
1556 #endif
1557 #ifdef HAVE_BZ2
1558 else if (d != NULL && d->bz2 != NULL)
1559 {
1560 int err;
1561 char const * const errmsg = BZ2_bzerror(d->bz2, &err);
1562 if (err != BZ_IO_ERROR)
1563 return FileFdError("BZ2_bzread: %s %s (%d: %s)", FileName.c_str(), _("Read error"), err, errmsg);
1564 }
1565 #endif
1566 #ifdef HAVE_LZMA
1567 else if (d != NULL && d->lzma != NULL)
1568 return FileFdError("lzma_read: %s (%d)", _("Read error"), d->lzma->err);
1569 #endif
1570 return FileFdErrno("read",_("Read error"));
1571 }
1572
1573 To = (char *)To + Res;
1574 Size -= Res;
1575 if (d != NULL)
1576 d->seekpos += Res;
1577 if (Actual != 0)
1578 *Actual += Res;
1579 }
1580 while (Res > 0 && Size > 0);
1581
1582 if (Size == 0)
1583 return true;
1584
1585 // Eof handling
1586 if (Actual != 0)
1587 {
1588 Flags |= HitEof;
1589 return true;
1590 }
1591
1592 return FileFdError(_("read, still have %llu to read but none left"), Size);
1593 }
1594 /*}}}*/
1595 // FileFd::ReadLine - Read a complete line from the file /*{{{*/
1596 // ---------------------------------------------------------------------
1597 /* Beware: This method can be quiet slow for big buffers on UNcompressed
1598 files because of the naive implementation! */
1599 char* FileFd::ReadLine(char *To, unsigned long long const Size)
1600 {
1601 *To = '\0';
1602 #ifdef HAVE_ZLIB
1603 if (d != NULL && d->gz != NULL)
1604 return gzgets(d->gz, To, Size);
1605 #endif
1606
1607 unsigned long long read = 0;
1608 while ((Size - 1) != read)
1609 {
1610 unsigned long long done = 0;
1611 if (Read(To + read, 1, &done) == false)
1612 return NULL;
1613 if (done == 0)
1614 break;
1615 if (To[read++] == '\n')
1616 break;
1617 }
1618 if (read == 0)
1619 return NULL;
1620 To[read] = '\0';
1621 return To;
1622 }
1623 /*}}}*/
1624 // FileFd::Write - Write to the file /*{{{*/
1625 // ---------------------------------------------------------------------
1626 /* */
1627 bool FileFd::Write(const void *From,unsigned long long Size)
1628 {
1629 ssize_t Res;
1630 errno = 0;
1631 do
1632 {
1633 if (false)
1634 /* dummy so that the rest can be 'else if's */;
1635 #ifdef HAVE_ZLIB
1636 else if (d != NULL && d->gz != NULL)
1637 Res = gzwrite(d->gz,From,Size);
1638 #endif
1639 #ifdef HAVE_BZ2
1640 else if (d != NULL && d->bz2 != NULL)
1641 Res = BZ2_bzwrite(d->bz2,(void*)From,Size);
1642 #endif
1643 #ifdef HAVE_LZMA
1644 else if (d != NULL && d->lzma != NULL)
1645 {
1646 d->lzma->stream.next_in = (uint8_t *)From;
1647 d->lzma->stream.avail_in = Size;
1648 d->lzma->stream.next_out = d->lzma->buffer;
1649 d->lzma->stream.avail_out = sizeof(d->lzma->buffer)/sizeof(d->lzma->buffer[0]);
1650 d->lzma->err = lzma_code(&d->lzma->stream, LZMA_RUN);
1651 if (d->lzma->err != LZMA_OK)
1652 return false;
1653 size_t const n = sizeof(d->lzma->buffer)/sizeof(d->lzma->buffer[0]) - d->lzma->stream.avail_out;
1654 size_t const m = (n == 0) ? 0 : fwrite(d->lzma->buffer, 1, n, d->lzma->file);
1655 if (m != n)
1656 Res = -1;
1657 else
1658 Res = Size - d->lzma->stream.avail_in;
1659 }
1660 #endif
1661 else
1662 Res = write(iFd,From,Size);
1663
1664 if (Res < 0 && errno == EINTR)
1665 continue;
1666 if (Res < 0)
1667 {
1668 if (false)
1669 /* dummy so that the rest can be 'else if's */;
1670 #ifdef HAVE_ZLIB
1671 else if (d != NULL && d->gz != NULL)
1672 {
1673 int err;
1674 char const * const errmsg = gzerror(d->gz, &err);
1675 if (err != Z_ERRNO)
1676 return FileFdError("gzwrite: %s (%d: %s)", _("Write error"), err, errmsg);
1677 }
1678 #endif
1679 #ifdef HAVE_BZ2
1680 else if (d != NULL && d->bz2 != NULL)
1681 {
1682 int err;
1683 char const * const errmsg = BZ2_bzerror(d->bz2, &err);
1684 if (err != BZ_IO_ERROR)
1685 return FileFdError("BZ2_bzwrite: %s (%d: %s)", _("Write error"), err, errmsg);
1686 }
1687 #endif
1688 #ifdef HAVE_LZMA
1689 else if (d != NULL && d->lzma != NULL)
1690 return FileFdErrno("lzma_fwrite", _("Write error"));
1691 #endif
1692 return FileFdErrno("write",_("Write error"));
1693 }
1694
1695 From = (char const *)From + Res;
1696 Size -= Res;
1697 if (d != NULL)
1698 d->seekpos += Res;
1699 }
1700 while (Res > 0 && Size > 0);
1701
1702 if (Size == 0)
1703 return true;
1704
1705 return FileFdError(_("write, still have %llu to write but couldn't"), Size);
1706 }
1707 bool FileFd::Write(int Fd, const void *From, unsigned long long Size)
1708 {
1709 ssize_t Res;
1710 errno = 0;
1711 do
1712 {
1713 Res = write(Fd,From,Size);
1714 if (Res < 0 && errno == EINTR)
1715 continue;
1716 if (Res < 0)
1717 return _error->Errno("write",_("Write error"));
1718
1719 From = (char const *)From + Res;
1720 Size -= Res;
1721 }
1722 while (Res > 0 && Size > 0);
1723
1724 if (Size == 0)
1725 return true;
1726
1727 return _error->Error(_("write, still have %llu to write but couldn't"), Size);
1728 }
1729 /*}}}*/
1730 // FileFd::Seek - Seek in the file /*{{{*/
1731 // ---------------------------------------------------------------------
1732 /* */
1733 bool FileFd::Seek(unsigned long long To)
1734 {
1735 Flags &= ~HitEof;
1736
1737 if (d != NULL && (d->pipe == true || d->InternalStream() == true))
1738 {
1739 // Our poor man seeking in pipes is costly, so try to avoid it
1740 unsigned long long seekpos = Tell();
1741 if (seekpos == To)
1742 return true;
1743 else if (seekpos < To)
1744 return Skip(To - seekpos);
1745
1746 if ((d->openmode & ReadOnly) != ReadOnly)
1747 return FileFdError("Reopen is only implemented for read-only files!");
1748 d->InternalClose(FileName);
1749 if (iFd != -1)
1750 close(iFd);
1751 iFd = -1;
1752 if (TemporaryFileName.empty() == false)
1753 iFd = open(TemporaryFileName.c_str(), O_RDONLY);
1754 else if (FileName.empty() == false)
1755 iFd = open(FileName.c_str(), O_RDONLY);
1756 else
1757 {
1758 if (d->compressed_fd > 0)
1759 if (lseek(d->compressed_fd, 0, SEEK_SET) != 0)
1760 iFd = d->compressed_fd;
1761 if (iFd < 0)
1762 return FileFdError("Reopen is not implemented for pipes opened with FileFd::OpenDescriptor()!");
1763 }
1764
1765 if (OpenInternDescriptor(d->openmode, d->compressor) == false)
1766 return FileFdError("Seek on file %s because it couldn't be reopened", FileName.c_str());
1767
1768 if (To != 0)
1769 return Skip(To);
1770
1771 d->seekpos = To;
1772 return true;
1773 }
1774 off_t res;
1775 #ifdef HAVE_ZLIB
1776 if (d != NULL && d->gz)
1777 res = gzseek(d->gz,To,SEEK_SET);
1778 else
1779 #endif
1780 res = lseek(iFd,To,SEEK_SET);
1781 if (res != (off_t)To)
1782 return FileFdError("Unable to seek to %llu", To);
1783
1784 if (d != NULL)
1785 d->seekpos = To;
1786 return true;
1787 }
1788 /*}}}*/
1789 // FileFd::Skip - Seek in the file /*{{{*/
1790 // ---------------------------------------------------------------------
1791 /* */
1792 bool FileFd::Skip(unsigned long long Over)
1793 {
1794 if (d != NULL && (d->pipe == true || d->InternalStream() == true))
1795 {
1796 char buffer[1024];
1797 while (Over != 0)
1798 {
1799 unsigned long long toread = std::min((unsigned long long) sizeof(buffer), Over);
1800 if (Read(buffer, toread) == false)
1801 return FileFdError("Unable to seek ahead %llu",Over);
1802 Over -= toread;
1803 }
1804 return true;
1805 }
1806
1807 off_t res;
1808 #ifdef HAVE_ZLIB
1809 if (d != NULL && d->gz != NULL)
1810 res = gzseek(d->gz,Over,SEEK_CUR);
1811 else
1812 #endif
1813 res = lseek(iFd,Over,SEEK_CUR);
1814 if (res < 0)
1815 return FileFdError("Unable to seek ahead %llu",Over);
1816 if (d != NULL)
1817 d->seekpos = res;
1818
1819 return true;
1820 }
1821 /*}}}*/
1822 // FileFd::Truncate - Truncate the file /*{{{*/
1823 // ---------------------------------------------------------------------
1824 /* */
1825 bool FileFd::Truncate(unsigned long long To)
1826 {
1827 // truncating /dev/null is always successful - as we get an error otherwise
1828 if (To == 0 && FileName == "/dev/null")
1829 return true;
1830 #if defined HAVE_ZLIB || defined HAVE_BZ2 || defined HAVE_LZMA
1831 if (d != NULL && (d->InternalStream() == true
1832 #ifdef HAVE_ZLIB
1833 || d->gz != NULL
1834 #endif
1835 ))
1836 return FileFdError("Truncating compressed files is not implemented (%s)", FileName.c_str());
1837 #endif
1838 if (ftruncate(iFd,To) != 0)
1839 return FileFdError("Unable to truncate to %llu",To);
1840
1841 return true;
1842 }
1843 /*}}}*/
1844 // FileFd::Tell - Current seek position /*{{{*/
1845 // ---------------------------------------------------------------------
1846 /* */
1847 unsigned long long FileFd::Tell()
1848 {
1849 // In theory, we could just return seekpos here always instead of
1850 // seeking around, but not all users of FileFd use always Seek() and co
1851 // so d->seekpos isn't always true and we can just use it as a hint if
1852 // we have nothing else, but not always as an authority…
1853 if (d != NULL && (d->pipe == true || d->InternalStream() == true))
1854 return d->seekpos;
1855
1856 off_t Res;
1857 #ifdef HAVE_ZLIB
1858 if (d != NULL && d->gz != NULL)
1859 Res = gztell(d->gz);
1860 else
1861 #endif
1862 Res = lseek(iFd,0,SEEK_CUR);
1863 if (Res == (off_t)-1)
1864 FileFdErrno("lseek","Failed to determine the current file position");
1865 if (d != NULL)
1866 d->seekpos = Res;
1867 return Res;
1868 }
1869 /*}}}*/
1870 static bool StatFileFd(char const * const msg, int const iFd, std::string const &FileName, struct stat &Buf, FileFdPrivate * const d) /*{{{*/
1871 {
1872 bool ispipe = (d != NULL && d->pipe == true);
1873 if (ispipe == false)
1874 {
1875 if (fstat(iFd,&Buf) != 0)
1876 // higher-level code will generate more meaningful messages,
1877 // even translated this would be meaningless for users
1878 return _error->Errno("fstat", "Unable to determine %s for fd %i", msg, iFd);
1879 if (FileName.empty() == false)
1880 ispipe = S_ISFIFO(Buf.st_mode);
1881 }
1882
1883 // for compressor pipes st_size is undefined and at 'best' zero
1884 if (ispipe == true)
1885 {
1886 // we set it here, too, as we get the info here for free
1887 // in theory the Open-methods should take care of it already
1888 if (d != NULL)
1889 d->pipe = true;
1890 if (stat(FileName.c_str(), &Buf) != 0)
1891 return _error->Errno("fstat", "Unable to determine %s for file %s", msg, FileName.c_str());
1892 }
1893 return true;
1894 }
1895 /*}}}*/
1896 // FileFd::FileSize - Return the size of the file /*{{{*/
1897 unsigned long long FileFd::FileSize()
1898 {
1899 struct stat Buf;
1900 if (StatFileFd("file size", iFd, FileName, Buf, d) == false)
1901 {
1902 Flags |= Fail;
1903 return 0;
1904 }
1905 return Buf.st_size;
1906 }
1907 /*}}}*/
1908 // FileFd::ModificationTime - Return the time of last touch /*{{{*/
1909 time_t FileFd::ModificationTime()
1910 {
1911 struct stat Buf;
1912 if (StatFileFd("modification time", iFd, FileName, Buf, d) == false)
1913 {
1914 Flags |= Fail;
1915 return 0;
1916 }
1917 return Buf.st_mtime;
1918 }
1919 /*}}}*/
1920 // FileFd::Size - Return the size of the content in the file /*{{{*/
1921 // ---------------------------------------------------------------------
1922 /* */
1923 unsigned long long FileFd::Size()
1924 {
1925 unsigned long long size = FileSize();
1926
1927 // for compressor pipes st_size is undefined and at 'best' zero,
1928 // so we 'read' the content and 'seek' back - see there
1929 if (d != NULL && (d->pipe == true || (d->InternalStream() == true && size > 0)))
1930 {
1931 unsigned long long const oldSeek = Tell();
1932 char ignore[1000];
1933 unsigned long long read = 0;
1934 do {
1935 if (Read(ignore, sizeof(ignore), &read) == false)
1936 {
1937 Seek(oldSeek);
1938 return 0;
1939 }
1940 } while(read != 0);
1941 size = Tell();
1942 Seek(oldSeek);
1943 }
1944 #ifdef HAVE_ZLIB
1945 // only check gzsize if we are actually a gzip file, just checking for
1946 // "gz" is not sufficient as uncompressed files could be opened with
1947 // gzopen in "direct" mode as well
1948 else if (d != NULL && d->gz && !gzdirect(d->gz) && size > 0)
1949 {
1950 off_t const oldPos = lseek(iFd,0,SEEK_CUR);
1951 /* unfortunately zlib.h doesn't provide a gzsize(), so we have to do
1952 * this ourselves; the original (uncompressed) file size is the last 32
1953 * bits of the file */
1954 // FIXME: Size for gz-files is limited by 32bit… no largefile support
1955 if (lseek(iFd, -4, SEEK_END) < 0)
1956 {
1957 FileFdErrno("lseek","Unable to seek to end of gzipped file");
1958 return 0;
1959 }
1960 uint32_t size = 0;
1961 if (read(iFd, &size, 4) != 4)
1962 {
1963 FileFdErrno("read","Unable to read original size of gzipped file");
1964 return 0;
1965 }
1966 size = le32toh(size);
1967
1968 if (lseek(iFd, oldPos, SEEK_SET) < 0)
1969 {
1970 FileFdErrno("lseek","Unable to seek in gzipped file");
1971 return 0;
1972 }
1973
1974 return size;
1975 }
1976 #endif
1977
1978 return size;
1979 }
1980 /*}}}*/
1981 // FileFd::Close - Close the file if the close flag is set /*{{{*/
1982 // ---------------------------------------------------------------------
1983 /* */
1984 bool FileFd::Close()
1985 {
1986 if (iFd == -1)
1987 return true;
1988
1989 bool Res = true;
1990 if ((Flags & AutoClose) == AutoClose)
1991 {
1992 if ((Flags & Compressed) != Compressed && iFd > 0 && close(iFd) != 0)
1993 Res &= _error->Errno("close",_("Problem closing the file %s"), FileName.c_str());
1994 if (d != NULL)
1995 {
1996 Res &= d->CloseDown(FileName);
1997 delete d;
1998 d = NULL;
1999 }
2000 }
2001
2002 if ((Flags & Replace) == Replace) {
2003 if (rename(TemporaryFileName.c_str(), FileName.c_str()) != 0)
2004 Res &= _error->Errno("rename",_("Problem renaming the file %s to %s"), TemporaryFileName.c_str(), FileName.c_str());
2005
2006 FileName = TemporaryFileName; // for the unlink() below.
2007 TemporaryFileName.clear();
2008 }
2009
2010 iFd = -1;
2011
2012 if ((Flags & Fail) == Fail && (Flags & DelOnFail) == DelOnFail &&
2013 FileName.empty() == false)
2014 if (unlink(FileName.c_str()) != 0)
2015 Res &= _error->WarningE("unlnk",_("Problem unlinking the file %s"), FileName.c_str());
2016
2017 if (Res == false)
2018 Flags |= Fail;
2019 return Res;
2020 }
2021 /*}}}*/
2022 // FileFd::Sync - Sync the file /*{{{*/
2023 // ---------------------------------------------------------------------
2024 /* */
2025 bool FileFd::Sync()
2026 {
2027 if (fsync(iFd) != 0)
2028 return FileFdErrno("sync",_("Problem syncing the file"));
2029 return true;
2030 }
2031 /*}}}*/
2032 // FileFd::FileFdErrno - set Fail and call _error->Errno *{{{*/
2033 bool FileFd::FileFdErrno(const char *Function, const char *Description,...)
2034 {
2035 Flags |= Fail;
2036 va_list args;
2037 size_t msgSize = 400;
2038 int const errsv = errno;
2039 while (true)
2040 {
2041 va_start(args,Description);
2042 if (_error->InsertErrno(GlobalError::ERROR, Function, Description, args, errsv, msgSize) == false)
2043 break;
2044 va_end(args);
2045 }
2046 return false;
2047 }
2048 /*}}}*/
2049 // FileFd::FileFdError - set Fail and call _error->Error *{{{*/
2050 bool FileFd::FileFdError(const char *Description,...) {
2051 Flags |= Fail;
2052 va_list args;
2053 size_t msgSize = 400;
2054 while (true)
2055 {
2056 va_start(args,Description);
2057 if (_error->Insert(GlobalError::ERROR, Description, args, msgSize) == false)
2058 break;
2059 va_end(args);
2060 }
2061 return false;
2062 }
2063 /*}}}*/
2064
2065 APT_DEPRECATED gzFile FileFd::gzFd() {
2066 #ifdef HAVE_ZLIB
2067 return d->gz;
2068 #else
2069 return NULL;
2070 #endif
2071 }
2072
2073 // Glob - wrapper around "glob()" /*{{{*/
2074 std::vector<std::string> Glob(std::string const &pattern, int flags)
2075 {
2076 std::vector<std::string> result;
2077 glob_t globbuf;
2078 int glob_res;
2079 unsigned int i;
2080
2081 glob_res = glob(pattern.c_str(), flags, NULL, &globbuf);
2082
2083 if (glob_res != 0)
2084 {
2085 if(glob_res != GLOB_NOMATCH) {
2086 _error->Errno("glob", "Problem with glob");
2087 return result;
2088 }
2089 }
2090
2091 // append results
2092 for(i=0;i<globbuf.gl_pathc;i++)
2093 result.push_back(string(globbuf.gl_pathv[i]));
2094
2095 globfree(&globbuf);
2096 return result;
2097 }
2098 /*}}}*/
2099 std::string GetTempDir() /*{{{*/
2100 {
2101 const char *tmpdir = getenv("TMPDIR");
2102
2103 #ifdef P_tmpdir
2104 if (!tmpdir)
2105 tmpdir = P_tmpdir;
2106 #endif
2107
2108 struct stat st;
2109 if (!tmpdir || strlen(tmpdir) == 0 || // tmpdir is set
2110 stat(tmpdir, &st) != 0 || (st.st_mode & S_IFDIR) == 0 || // exists and is directory
2111 access(tmpdir, R_OK | W_OK | X_OK) != 0 // current user has rwx access to directory
2112 )
2113 tmpdir = "/tmp";
2114
2115 return string(tmpdir);
2116 }
2117 /*}}}*/
2118 FileFd* GetTempFile(std::string const &Prefix, bool ImmediateUnlink) /*{{{*/
2119 {
2120 char fn[512];
2121 FileFd *Fd = new FileFd();
2122
2123 std::string tempdir = GetTempDir();
2124 snprintf(fn, sizeof(fn), "%s/%s.XXXXXX",
2125 tempdir.c_str(), Prefix.c_str());
2126 int fd = mkstemp(fn);
2127 if(ImmediateUnlink)
2128 unlink(fn);
2129 if (fd < 0)
2130 {
2131 _error->Errno("GetTempFile",_("Unable to mkstemp %s"), fn);
2132 return NULL;
2133 }
2134 if (!Fd->OpenDescriptor(fd, FileFd::WriteOnly, FileFd::None, true))
2135 {
2136 _error->Errno("GetTempFile",_("Unable to write to %s"),fn);
2137 return NULL;
2138 }
2139
2140 return Fd;
2141 }
2142 /*}}}*/
2143 bool Rename(std::string From, std::string To) /*{{{*/
2144 {
2145 if (rename(From.c_str(),To.c_str()) != 0)
2146 {
2147 _error->Error(_("rename failed, %s (%s -> %s)."),strerror(errno),
2148 From.c_str(),To.c_str());
2149 return false;
2150 }
2151 return true;
2152 }
2153 /*}}}*/
2154 bool Popen(const char* Args[], FileFd &Fd, pid_t &Child, FileFd::OpenMode Mode)/*{{{*/
2155 {
2156 int fd;
2157 if (Mode != FileFd::ReadOnly && Mode != FileFd::WriteOnly)
2158 return _error->Error("Popen supports ReadOnly (x)or WriteOnly mode only");
2159
2160 int Pipe[2] = {-1, -1};
2161 if(pipe(Pipe) != 0)
2162 return _error->Errno("pipe", _("Failed to create subprocess IPC"));
2163
2164 std::set<int> keep_fds;
2165 keep_fds.insert(Pipe[0]);
2166 keep_fds.insert(Pipe[1]);
2167 Child = ExecFork(keep_fds);
2168 if(Child < 0)
2169 return _error->Errno("fork", "Failed to fork");
2170 if(Child == 0)
2171 {
2172 if(Mode == FileFd::ReadOnly)
2173 {
2174 close(Pipe[0]);
2175 fd = Pipe[1];
2176 }
2177 else if(Mode == FileFd::WriteOnly)
2178 {
2179 close(Pipe[1]);
2180 fd = Pipe[0];
2181 }
2182
2183 if(Mode == FileFd::ReadOnly)
2184 {
2185 dup2(fd, 1);
2186 dup2(fd, 2);
2187 } else if(Mode == FileFd::WriteOnly)
2188 dup2(fd, 0);
2189
2190 execv(Args[0], (char**)Args);
2191 _exit(100);
2192 }
2193 if(Mode == FileFd::ReadOnly)
2194 {
2195 close(Pipe[1]);
2196 fd = Pipe[0];
2197 } else if(Mode == FileFd::WriteOnly)
2198 {
2199 close(Pipe[0]);
2200 fd = Pipe[1];
2201 }
2202 Fd.OpenDescriptor(fd, Mode, FileFd::None, true);
2203
2204 return true;
2205 }
2206 /*}}}*/
2207 bool DropPrivileges() /*{{{*/
2208 {
2209 if(_config->FindB("Debug::NoDropPrivs", false) == true)
2210 return true;
2211
2212 #if __gnu_linux__
2213 #if defined(PR_SET_NO_NEW_PRIVS) && ( PR_SET_NO_NEW_PRIVS != 38 )
2214 #error "PR_SET_NO_NEW_PRIVS is defined, but with a different value than expected!"
2215 #endif
2216 // see prctl(2), needs linux3.5 at runtime - magic constant to avoid it at buildtime
2217 int ret = prctl(38, 1, 0, 0, 0);
2218 // ignore EINVAL - kernel is too old to understand the option
2219 if(ret < 0 && errno != EINVAL)
2220 _error->Warning("PR_SET_NO_NEW_PRIVS failed with %i", ret);
2221 #endif
2222
2223 // empty setting disables privilege dropping - this also ensures
2224 // backward compatibility, see bug #764506
2225 const std::string toUser = _config->Find("APT::Sandbox::User");
2226 if (toUser.empty())
2227 return true;
2228
2229 // uid will be 0 in the end, but gid might be different anyway
2230 uid_t const old_uid = getuid();
2231 gid_t const old_gid = getgid();
2232
2233 if (old_uid != 0)
2234 return true;
2235
2236 struct passwd *pw = getpwnam(toUser.c_str());
2237 if (pw == NULL)
2238 return _error->Error("No user %s, can not drop rights", toUser.c_str());
2239
2240 // Do not change the order here, it might break things
2241 if (setgroups(1, &pw->pw_gid))
2242 return _error->Errno("setgroups", "Failed to setgroups");
2243
2244 if (setegid(pw->pw_gid) != 0)
2245 return _error->Errno("setegid", "Failed to setegid");
2246
2247 if (setgid(pw->pw_gid) != 0)
2248 return _error->Errno("setgid", "Failed to setgid");
2249
2250 if (setuid(pw->pw_uid) != 0)
2251 return _error->Errno("setuid", "Failed to setuid");
2252
2253 // the seteuid() is probably uneeded (at least thats what the linux
2254 // man-page says about setuid(2)) but we cargo culted it anyway
2255 if (seteuid(pw->pw_uid) != 0)
2256 return _error->Errno("seteuid", "Failed to seteuid");
2257
2258 // Verify that the user has only a single group, and the correct one
2259 gid_t groups[1];
2260 if (getgroups(1, groups) != 1)
2261 return _error->Errno("getgroups", "Could not get new groups");
2262 if (groups[0] != pw->pw_gid)
2263 return _error->Error("Could not switch group");
2264
2265 // Verify that gid, egid, uid, and euid changed
2266 if (getgid() != pw->pw_gid)
2267 return _error->Error("Could not switch group");
2268 if (getegid() != pw->pw_gid)
2269 return _error->Error("Could not switch effective group");
2270 if (getuid() != pw->pw_uid)
2271 return _error->Error("Could not switch user");
2272 if (geteuid() != pw->pw_uid)
2273 return _error->Error("Could not switch effective user");
2274
2275 #ifdef HAVE_GETRESUID
2276 // verify that the saved set-user-id was changed as well
2277 uid_t ruid = 0;
2278 uid_t euid = 0;
2279 uid_t suid = 0;
2280 if (getresuid(&ruid, &euid, &suid))
2281 return _error->Errno("getresuid", "Could not get saved set-user-ID");
2282 if (suid != pw->pw_uid)
2283 return _error->Error("Could not switch saved set-user-ID");
2284 #endif
2285
2286 #ifdef HAVE_GETRESGID
2287 // verify that the saved set-group-id was changed as well
2288 gid_t rgid = 0;
2289 gid_t egid = 0;
2290 gid_t sgid = 0;
2291 if (getresgid(&rgid, &egid, &sgid))
2292 return _error->Errno("getresuid", "Could not get saved set-group-ID");
2293 if (sgid != pw->pw_gid)
2294 return _error->Error("Could not switch saved set-group-ID");
2295 #endif
2296
2297 // Check that uid and gid changes do not work anymore
2298 if (pw->pw_gid != old_gid && (setgid(old_gid) != -1 || setegid(old_gid) != -1))
2299 return _error->Error("Could restore a gid to root, privilege dropping did not work");
2300
2301 if (pw->pw_uid != old_uid && (setuid(old_uid) != -1 || seteuid(old_uid) != -1))
2302 return _error->Error("Could restore a uid to root, privilege dropping did not work");
2303
2304 return true;
2305 }
2306 /*}}}*/