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