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