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