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