]> git.saurik.com Git - apt.git/blob - apt-pkg/contrib/fileutl.cc
Use mkstemp() in apt-extracttemplaes (closes: #741627)
[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 (AutoClose == false && 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 Res = Size - d->lzma->stream.avail_out;
1434 }
1435 #endif
1436 else
1437 Res = read(iFd,To,Size);
1438
1439 if (Res < 0)
1440 {
1441 if (errno == EINTR)
1442 continue;
1443 if (false)
1444 /* dummy so that the rest can be 'else if's */;
1445 #ifdef HAVE_ZLIB
1446 else if (d != NULL && d->gz != NULL)
1447 {
1448 int err;
1449 char const * const errmsg = gzerror(d->gz, &err);
1450 if (err != Z_ERRNO)
1451 return FileFdError("gzread: %s (%d: %s)", _("Read error"), err, errmsg);
1452 }
1453 #endif
1454 #ifdef HAVE_BZ2
1455 else if (d != NULL && d->bz2 != NULL)
1456 {
1457 int err;
1458 char const * const errmsg = BZ2_bzerror(d->bz2, &err);
1459 if (err != BZ_IO_ERROR)
1460 return FileFdError("BZ2_bzread: %s (%d: %s)", _("Read error"), err, errmsg);
1461 }
1462 #endif
1463 #ifdef HAVE_LZMA
1464 else if (d != NULL && d->lzma != NULL)
1465 return FileFdError("lzma_read: %s (%d)", _("Read error"), d->lzma->err);
1466 #endif
1467 return FileFdErrno("read",_("Read error"));
1468 }
1469
1470 To = (char *)To + Res;
1471 Size -= Res;
1472 if (d != NULL)
1473 d->seekpos += Res;
1474 if (Actual != 0)
1475 *Actual += Res;
1476 }
1477 while (Res > 0 && Size > 0);
1478
1479 if (Size == 0)
1480 return true;
1481
1482 // Eof handling
1483 if (Actual != 0)
1484 {
1485 Flags |= HitEof;
1486 return true;
1487 }
1488
1489 return FileFdError(_("read, still have %llu to read but none left"), Size);
1490 }
1491 /*}}}*/
1492 // FileFd::ReadLine - Read a complete line from the file /*{{{*/
1493 // ---------------------------------------------------------------------
1494 /* Beware: This method can be quiet slow for big buffers on UNcompressed
1495 files because of the naive implementation! */
1496 char* FileFd::ReadLine(char *To, unsigned long long const Size)
1497 {
1498 *To = '\0';
1499 #ifdef HAVE_ZLIB
1500 if (d != NULL && d->gz != NULL)
1501 return gzgets(d->gz, To, Size);
1502 #endif
1503
1504 unsigned long long read = 0;
1505 while ((Size - 1) != read)
1506 {
1507 unsigned long long done = 0;
1508 if (Read(To + read, 1, &done) == false)
1509 return NULL;
1510 if (done == 0)
1511 break;
1512 if (To[read++] == '\n')
1513 break;
1514 }
1515 if (read == 0)
1516 return NULL;
1517 To[read] = '\0';
1518 return To;
1519 }
1520 /*}}}*/
1521 // FileFd::Write - Write to the file /*{{{*/
1522 // ---------------------------------------------------------------------
1523 /* */
1524 bool FileFd::Write(const void *From,unsigned long long Size)
1525 {
1526 ssize_t Res;
1527 errno = 0;
1528 do
1529 {
1530 if (false)
1531 /* dummy so that the rest can be 'else if's */;
1532 #ifdef HAVE_ZLIB
1533 else if (d != NULL && d->gz != NULL)
1534 Res = gzwrite(d->gz,From,Size);
1535 #endif
1536 #ifdef HAVE_BZ2
1537 else if (d != NULL && d->bz2 != NULL)
1538 Res = BZ2_bzwrite(d->bz2,(void*)From,Size);
1539 #endif
1540 #ifdef HAVE_LZMA
1541 else if (d != NULL && d->lzma != NULL)
1542 {
1543 d->lzma->stream.next_in = (uint8_t *)From;
1544 d->lzma->stream.avail_in = Size;
1545 d->lzma->stream.next_out = d->lzma->buffer;
1546 d->lzma->stream.avail_out = sizeof(d->lzma->buffer)/sizeof(d->lzma->buffer[0]);
1547 d->lzma->err = lzma_code(&d->lzma->stream, LZMA_RUN);
1548 if (d->lzma->err != LZMA_OK)
1549 return false;
1550 size_t const n = sizeof(d->lzma->buffer)/sizeof(d->lzma->buffer[0]) - d->lzma->stream.avail_out;
1551 size_t const m = (n == 0) ? 0 : fwrite(d->lzma->buffer, 1, n, d->lzma->file);
1552 if (m != n)
1553 Res = -1;
1554 else
1555 Res = Size - d->lzma->stream.avail_in;
1556 }
1557 #endif
1558 else
1559 Res = write(iFd,From,Size);
1560
1561 if (Res < 0 && errno == EINTR)
1562 continue;
1563 if (Res < 0)
1564 {
1565 if (false)
1566 /* dummy so that the rest can be 'else if's */;
1567 #ifdef HAVE_ZLIB
1568 else if (d != NULL && d->gz != NULL)
1569 {
1570 int err;
1571 char const * const errmsg = gzerror(d->gz, &err);
1572 if (err != Z_ERRNO)
1573 return FileFdError("gzwrite: %s (%d: %s)", _("Write error"), err, errmsg);
1574 }
1575 #endif
1576 #ifdef HAVE_BZ2
1577 else if (d != NULL && d->bz2 != NULL)
1578 {
1579 int err;
1580 char const * const errmsg = BZ2_bzerror(d->bz2, &err);
1581 if (err != BZ_IO_ERROR)
1582 return FileFdError("BZ2_bzwrite: %s (%d: %s)", _("Write error"), err, errmsg);
1583 }
1584 #endif
1585 #ifdef HAVE_LZMA
1586 else if (d != NULL && d->lzma != NULL)
1587 return FileFdErrno("lzma_fwrite", _("Write error"));
1588 #endif
1589 return FileFdErrno("write",_("Write error"));
1590 }
1591
1592 From = (char const *)From + Res;
1593 Size -= Res;
1594 if (d != NULL)
1595 d->seekpos += Res;
1596 }
1597 while (Res > 0 && Size > 0);
1598
1599 if (Size == 0)
1600 return true;
1601
1602 return FileFdError(_("write, still have %llu to write but couldn't"), Size);
1603 }
1604 bool FileFd::Write(int Fd, const void *From, unsigned long long Size)
1605 {
1606 ssize_t Res;
1607 errno = 0;
1608 do
1609 {
1610 Res = write(Fd,From,Size);
1611 if (Res < 0 && errno == EINTR)
1612 continue;
1613 if (Res < 0)
1614 return _error->Errno("write",_("Write error"));
1615
1616 From = (char const *)From + Res;
1617 Size -= Res;
1618 }
1619 while (Res > 0 && Size > 0);
1620
1621 if (Size == 0)
1622 return true;
1623
1624 return _error->Error(_("write, still have %llu to write but couldn't"), Size);
1625 }
1626 /*}}}*/
1627 // FileFd::Seek - Seek in the file /*{{{*/
1628 // ---------------------------------------------------------------------
1629 /* */
1630 bool FileFd::Seek(unsigned long long To)
1631 {
1632 if (d != NULL && (d->pipe == true || d->InternalStream() == true))
1633 {
1634 // Our poor man seeking in pipes is costly, so try to avoid it
1635 unsigned long long seekpos = Tell();
1636 if (seekpos == To)
1637 return true;
1638 else if (seekpos < To)
1639 return Skip(To - seekpos);
1640
1641 if ((d->openmode & ReadOnly) != ReadOnly)
1642 return FileFdError("Reopen is only implemented for read-only files!");
1643 d->InternalClose(FileName);
1644 if (iFd != -1)
1645 close(iFd);
1646 iFd = -1;
1647 if (TemporaryFileName.empty() == false)
1648 iFd = open(TemporaryFileName.c_str(), O_RDONLY);
1649 else if (FileName.empty() == false)
1650 iFd = open(FileName.c_str(), O_RDONLY);
1651 else
1652 {
1653 if (d->compressed_fd > 0)
1654 if (lseek(d->compressed_fd, 0, SEEK_SET) != 0)
1655 iFd = d->compressed_fd;
1656 if (iFd < 0)
1657 return FileFdError("Reopen is not implemented for pipes opened with FileFd::OpenDescriptor()!");
1658 }
1659
1660 if (OpenInternDescriptor(d->openmode, d->compressor) == false)
1661 return FileFdError("Seek on file %s because it couldn't be reopened", FileName.c_str());
1662
1663 if (To != 0)
1664 return Skip(To);
1665
1666 d->seekpos = To;
1667 return true;
1668 }
1669 off_t res;
1670 #ifdef HAVE_ZLIB
1671 if (d != NULL && d->gz)
1672 res = gzseek(d->gz,To,SEEK_SET);
1673 else
1674 #endif
1675 res = lseek(iFd,To,SEEK_SET);
1676 if (res != (off_t)To)
1677 return FileFdError("Unable to seek to %llu", To);
1678
1679 if (d != NULL)
1680 d->seekpos = To;
1681 return true;
1682 }
1683 /*}}}*/
1684 // FileFd::Skip - Seek in the file /*{{{*/
1685 // ---------------------------------------------------------------------
1686 /* */
1687 bool FileFd::Skip(unsigned long long Over)
1688 {
1689 if (d != NULL && (d->pipe == true || d->InternalStream() == true))
1690 {
1691 d->seekpos += Over;
1692 char buffer[1024];
1693 while (Over != 0)
1694 {
1695 unsigned long long toread = std::min((unsigned long long) sizeof(buffer), Over);
1696 if (Read(buffer, toread) == false)
1697 return FileFdError("Unable to seek ahead %llu",Over);
1698 Over -= toread;
1699 }
1700 return true;
1701 }
1702
1703 off_t res;
1704 #ifdef HAVE_ZLIB
1705 if (d != NULL && d->gz != NULL)
1706 res = gzseek(d->gz,Over,SEEK_CUR);
1707 else
1708 #endif
1709 res = lseek(iFd,Over,SEEK_CUR);
1710 if (res < 0)
1711 return FileFdError("Unable to seek ahead %llu",Over);
1712 if (d != NULL)
1713 d->seekpos = res;
1714
1715 return true;
1716 }
1717 /*}}}*/
1718 // FileFd::Truncate - Truncate the file /*{{{*/
1719 // ---------------------------------------------------------------------
1720 /* */
1721 bool FileFd::Truncate(unsigned long long To)
1722 {
1723 // truncating /dev/null is always successful - as we get an error otherwise
1724 if (To == 0 && FileName == "/dev/null")
1725 return true;
1726 #if defined HAVE_ZLIB || defined HAVE_BZ2 || defined HAVE_LZMA
1727 if (d != NULL && (d->InternalStream() == true
1728 #ifdef HAVE_ZLIB
1729 || d->gz != NULL
1730 #endif
1731 ))
1732 return FileFdError("Truncating compressed files is not implemented (%s)", FileName.c_str());
1733 #endif
1734 if (ftruncate(iFd,To) != 0)
1735 return FileFdError("Unable to truncate to %llu",To);
1736
1737 return true;
1738 }
1739 /*}}}*/
1740 // FileFd::Tell - Current seek position /*{{{*/
1741 // ---------------------------------------------------------------------
1742 /* */
1743 unsigned long long FileFd::Tell()
1744 {
1745 // In theory, we could just return seekpos here always instead of
1746 // seeking around, but not all users of FileFd use always Seek() and co
1747 // so d->seekpos isn't always true and we can just use it as a hint if
1748 // we have nothing else, but not always as an authority…
1749 if (d != NULL && (d->pipe == true || d->InternalStream() == true))
1750 return d->seekpos;
1751
1752 off_t Res;
1753 #ifdef HAVE_ZLIB
1754 if (d != NULL && d->gz != NULL)
1755 Res = gztell(d->gz);
1756 else
1757 #endif
1758 Res = lseek(iFd,0,SEEK_CUR);
1759 if (Res == (off_t)-1)
1760 FileFdErrno("lseek","Failed to determine the current file position");
1761 if (d != NULL)
1762 d->seekpos = Res;
1763 return Res;
1764 }
1765 /*}}}*/
1766 static bool StatFileFd(char const * const msg, int const iFd, std::string const &FileName, struct stat &Buf, FileFdPrivate * const d) /*{{{*/
1767 {
1768 bool ispipe = (d != NULL && d->pipe == true);
1769 if (ispipe == false)
1770 {
1771 if (fstat(iFd,&Buf) != 0)
1772 // higher-level code will generate more meaningful messages,
1773 // even translated this would be meaningless for users
1774 return _error->Errno("fstat", "Unable to determine %s for fd %i", msg, iFd);
1775 ispipe = S_ISFIFO(Buf.st_mode);
1776 }
1777
1778 // for compressor pipes st_size is undefined and at 'best' zero
1779 if (ispipe == true)
1780 {
1781 // we set it here, too, as we get the info here for free
1782 // in theory the Open-methods should take care of it already
1783 if (d != NULL)
1784 d->pipe = true;
1785 if (stat(FileName.c_str(), &Buf) != 0)
1786 return _error->Errno("fstat", "Unable to determine %s for file %s", msg, FileName.c_str());
1787 }
1788 return true;
1789 }
1790 /*}}}*/
1791 // FileFd::FileSize - Return the size of the file /*{{{*/
1792 unsigned long long FileFd::FileSize()
1793 {
1794 struct stat Buf;
1795 if (StatFileFd("file size", iFd, FileName, Buf, d) == false)
1796 {
1797 Flags |= Fail;
1798 return 0;
1799 }
1800 return Buf.st_size;
1801 }
1802 /*}}}*/
1803 // FileFd::ModificationTime - Return the time of last touch /*{{{*/
1804 time_t FileFd::ModificationTime()
1805 {
1806 struct stat Buf;
1807 if (StatFileFd("modification time", iFd, FileName, Buf, d) == false)
1808 {
1809 Flags |= Fail;
1810 return 0;
1811 }
1812 return Buf.st_mtime;
1813 }
1814 /*}}}*/
1815 // FileFd::Size - Return the size of the content in the file /*{{{*/
1816 // ---------------------------------------------------------------------
1817 /* */
1818 unsigned long long FileFd::Size()
1819 {
1820 unsigned long long size = FileSize();
1821
1822 // for compressor pipes st_size is undefined and at 'best' zero,
1823 // so we 'read' the content and 'seek' back - see there
1824 if (d != NULL && (d->pipe == true || (d->InternalStream() == true && size > 0)))
1825 {
1826 unsigned long long const oldSeek = Tell();
1827 char ignore[1000];
1828 unsigned long long read = 0;
1829 do {
1830 if (Read(ignore, sizeof(ignore), &read) == false)
1831 {
1832 Seek(oldSeek);
1833 return 0;
1834 }
1835 } while(read != 0);
1836 size = Tell();
1837 Seek(oldSeek);
1838 }
1839 #ifdef HAVE_ZLIB
1840 // only check gzsize if we are actually a gzip file, just checking for
1841 // "gz" is not sufficient as uncompressed files could be opened with
1842 // gzopen in "direct" mode as well
1843 else if (d != NULL && d->gz && !gzdirect(d->gz) && size > 0)
1844 {
1845 off_t const oldPos = lseek(iFd,0,SEEK_CUR);
1846 /* unfortunately zlib.h doesn't provide a gzsize(), so we have to do
1847 * this ourselves; the original (uncompressed) file size is the last 32
1848 * bits of the file */
1849 // FIXME: Size for gz-files is limited by 32bit… no largefile support
1850 if (lseek(iFd, -4, SEEK_END) < 0)
1851 {
1852 FileFdErrno("lseek","Unable to seek to end of gzipped file");
1853 return 0;
1854 }
1855 size = 0;
1856 if (read(iFd, &size, 4) != 4)
1857 {
1858 FileFdErrno("read","Unable to read original size of gzipped file");
1859 return 0;
1860 }
1861
1862 #ifdef WORDS_BIGENDIAN
1863 uint32_t tmp_size = size;
1864 uint8_t const * const p = (uint8_t const * const) &tmp_size;
1865 tmp_size = (p[3] << 24) | (p[2] << 16) | (p[1] << 8) | p[0];
1866 size = tmp_size;
1867 #endif
1868
1869 if (lseek(iFd, oldPos, SEEK_SET) < 0)
1870 {
1871 FileFdErrno("lseek","Unable to seek in gzipped file");
1872 return 0;
1873 }
1874
1875 return size;
1876 }
1877 #endif
1878
1879 return size;
1880 }
1881 /*}}}*/
1882 // FileFd::Close - Close the file if the close flag is set /*{{{*/
1883 // ---------------------------------------------------------------------
1884 /* */
1885 bool FileFd::Close()
1886 {
1887 if (iFd == -1)
1888 return true;
1889
1890 bool Res = true;
1891 if ((Flags & AutoClose) == AutoClose)
1892 {
1893 if ((Flags & Compressed) != Compressed && iFd > 0 && close(iFd) != 0)
1894 Res &= _error->Errno("close",_("Problem closing the file %s"), FileName.c_str());
1895
1896 if (d != NULL)
1897 {
1898 Res &= d->CloseDown(FileName);
1899 delete d;
1900 d = NULL;
1901 }
1902 }
1903
1904 if ((Flags & Replace) == Replace) {
1905 if (rename(TemporaryFileName.c_str(), FileName.c_str()) != 0)
1906 Res &= _error->Errno("rename",_("Problem renaming the file %s to %s"), TemporaryFileName.c_str(), FileName.c_str());
1907
1908 FileName = TemporaryFileName; // for the unlink() below.
1909 TemporaryFileName.clear();
1910 }
1911
1912 iFd = -1;
1913
1914 if ((Flags & Fail) == Fail && (Flags & DelOnFail) == DelOnFail &&
1915 FileName.empty() == false)
1916 if (unlink(FileName.c_str()) != 0)
1917 Res &= _error->WarningE("unlnk",_("Problem unlinking the file %s"), FileName.c_str());
1918
1919 if (Res == false)
1920 Flags |= Fail;
1921 return Res;
1922 }
1923 /*}}}*/
1924 // FileFd::Sync - Sync the file /*{{{*/
1925 // ---------------------------------------------------------------------
1926 /* */
1927 bool FileFd::Sync()
1928 {
1929 if (fsync(iFd) != 0)
1930 return FileFdErrno("sync",_("Problem syncing the file"));
1931 return true;
1932 }
1933 /*}}}*/
1934 // FileFd::FileFdErrno - set Fail and call _error->Errno *{{{*/
1935 bool FileFd::FileFdErrno(const char *Function, const char *Description,...)
1936 {
1937 Flags |= Fail;
1938 va_list args;
1939 size_t msgSize = 400;
1940 int const errsv = errno;
1941 while (true)
1942 {
1943 va_start(args,Description);
1944 if (_error->InsertErrno(GlobalError::ERROR, Function, Description, args, errsv, msgSize) == false)
1945 break;
1946 va_end(args);
1947 }
1948 return false;
1949 }
1950 /*}}}*/
1951 // FileFd::FileFdError - set Fail and call _error->Error *{{{*/
1952 bool FileFd::FileFdError(const char *Description,...) {
1953 Flags |= Fail;
1954 va_list args;
1955 size_t msgSize = 400;
1956 while (true)
1957 {
1958 va_start(args,Description);
1959 if (_error->Insert(GlobalError::ERROR, Description, args, msgSize) == false)
1960 break;
1961 va_end(args);
1962 }
1963 return false;
1964 }
1965 /*}}}*/
1966
1967 APT_DEPRECATED gzFile FileFd::gzFd() {
1968 #ifdef HAVE_ZLIB
1969 return d->gz;
1970 #else
1971 return NULL;
1972 #endif
1973 }
1974
1975
1976 // Glob - wrapper around "glob()" /*{{{*/
1977 // ---------------------------------------------------------------------
1978 /* */
1979 std::vector<std::string> Glob(std::string const &pattern, int flags)
1980 {
1981 std::vector<std::string> result;
1982 glob_t globbuf;
1983 int glob_res;
1984 unsigned int i;
1985
1986 glob_res = glob(pattern.c_str(), flags, NULL, &globbuf);
1987
1988 if (glob_res != 0)
1989 {
1990 if(glob_res != GLOB_NOMATCH) {
1991 _error->Errno("glob", "Problem with glob");
1992 return result;
1993 }
1994 }
1995
1996 // append results
1997 for(i=0;i<globbuf.gl_pathc;i++)
1998 result.push_back(string(globbuf.gl_pathv[i]));
1999
2000 globfree(&globbuf);
2001 return result;
2002 }
2003 /*}}}*/
2004
2005 std::string GetTempDir()
2006 {
2007 const char *tmpdir = getenv("TMPDIR");
2008
2009 #ifdef P_tmpdir
2010 if (!tmpdir)
2011 tmpdir = P_tmpdir;
2012 #endif
2013
2014 // check that tmpdir is set and exists
2015 struct stat st;
2016 if (!tmpdir || strlen(tmpdir) == 0 || stat(tmpdir, &st) != 0)
2017 tmpdir = "/tmp";
2018
2019 return string(tmpdir);
2020 }
2021
2022 bool Rename(std::string From, std::string To)
2023 {
2024 if (rename(From.c_str(),To.c_str()) != 0)
2025 {
2026 _error->Error(_("rename failed, %s (%s -> %s)."),strerror(errno),
2027 From.c_str(),To.c_str());
2028 return false;
2029 }
2030 return true;
2031 }