]> git.saurik.com Git - apt.git/blob - apt-pkg/contrib/fileutl.cc
use flAbsPath()
[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 // flAbsPath - Return the absolute path of the filename /*{{{*/
663 // ---------------------------------------------------------------------
664 /* */
665 string flAbsPath(string File)
666 {
667 char *p = realpath(File.c_str(), NULL);
668 if (p == NULL)
669 {
670 _error->Errno("realpath", "flAbsPath failed");
671 return "";
672 }
673 std::string AbsPath(p);
674 free(p);
675 return AbsPath;
676 }
677 /*}}}*/
678 // SetCloseExec - Set the close on exec flag /*{{{*/
679 // ---------------------------------------------------------------------
680 /* */
681 void SetCloseExec(int Fd,bool Close)
682 {
683 if (fcntl(Fd,F_SETFD,(Close == false)?0:FD_CLOEXEC) != 0)
684 {
685 cerr << "FATAL -> Could not set close on exec " << strerror(errno) << endl;
686 exit(100);
687 }
688 }
689 /*}}}*/
690 // SetNonBlock - Set the nonblocking flag /*{{{*/
691 // ---------------------------------------------------------------------
692 /* */
693 void SetNonBlock(int Fd,bool Block)
694 {
695 int Flags = fcntl(Fd,F_GETFL) & (~O_NONBLOCK);
696 if (fcntl(Fd,F_SETFL,Flags | ((Block == false)?0:O_NONBLOCK)) != 0)
697 {
698 cerr << "FATAL -> Could not set non-blocking flag " << strerror(errno) << endl;
699 exit(100);
700 }
701 }
702 /*}}}*/
703 // WaitFd - Wait for a FD to become readable /*{{{*/
704 // ---------------------------------------------------------------------
705 /* This waits for a FD to become readable using select. It is useful for
706 applications making use of non-blocking sockets. The timeout is
707 in seconds. */
708 bool WaitFd(int Fd,bool write,unsigned long timeout)
709 {
710 fd_set Set;
711 struct timeval tv;
712 FD_ZERO(&Set);
713 FD_SET(Fd,&Set);
714 tv.tv_sec = timeout;
715 tv.tv_usec = 0;
716 if (write == true)
717 {
718 int Res;
719 do
720 {
721 Res = select(Fd+1,0,&Set,0,(timeout != 0?&tv:0));
722 }
723 while (Res < 0 && errno == EINTR);
724
725 if (Res <= 0)
726 return false;
727 }
728 else
729 {
730 int Res;
731 do
732 {
733 Res = select(Fd+1,&Set,0,0,(timeout != 0?&tv:0));
734 }
735 while (Res < 0 && errno == EINTR);
736
737 if (Res <= 0)
738 return false;
739 }
740
741 return true;
742 }
743 /*}}}*/
744 // MergeKeepFdsFromConfiguration - Merge APT::Keep-Fds configuration /*{{{*/
745 // ---------------------------------------------------------------------
746 /* This is used to merge the APT::Keep-Fds with the provided KeepFDs
747 * set.
748 */
749 void MergeKeepFdsFromConfiguration(std::set<int> &KeepFDs)
750 {
751 Configuration::Item const *Opts = _config->Tree("APT::Keep-Fds");
752 if (Opts != 0 && Opts->Child != 0)
753 {
754 Opts = Opts->Child;
755 for (; Opts != 0; Opts = Opts->Next)
756 {
757 if (Opts->Value.empty() == true)
758 continue;
759 int fd = atoi(Opts->Value.c_str());
760 KeepFDs.insert(fd);
761 }
762 }
763 }
764 /*}}}*/
765 // ExecFork - Magical fork that sanitizes the context before execing /*{{{*/
766 // ---------------------------------------------------------------------
767 /* This is used if you want to cleanse the environment for the forked
768 child, it fixes up the important signals and nukes all of the fds,
769 otherwise acts like normal fork. */
770 pid_t ExecFork()
771 {
772 set<int> KeepFDs;
773 // we need to merge the Keep-Fds as external tools like
774 // debconf-apt-progress use it
775 MergeKeepFdsFromConfiguration(KeepFDs);
776 return ExecFork(KeepFDs);
777 }
778
779 pid_t ExecFork(std::set<int> KeepFDs)
780 {
781 // Fork off the process
782 pid_t Process = fork();
783 if (Process < 0)
784 {
785 cerr << "FATAL -> Failed to fork." << endl;
786 exit(100);
787 }
788
789 // Spawn the subprocess
790 if (Process == 0)
791 {
792 // Setup the signals
793 signal(SIGPIPE,SIG_DFL);
794 signal(SIGQUIT,SIG_DFL);
795 signal(SIGINT,SIG_DFL);
796 signal(SIGWINCH,SIG_DFL);
797 signal(SIGCONT,SIG_DFL);
798 signal(SIGTSTP,SIG_DFL);
799
800 // Close all of our FDs - just in case
801 for (int K = 3; K != sysconf(_SC_OPEN_MAX); K++)
802 {
803 if(KeepFDs.find(K) == KeepFDs.end())
804 fcntl(K,F_SETFD,FD_CLOEXEC);
805 }
806 }
807
808 return Process;
809 }
810 /*}}}*/
811 // ExecWait - Fancy waitpid /*{{{*/
812 // ---------------------------------------------------------------------
813 /* Waits for the given sub process. If Reap is set then no errors are
814 generated. Otherwise a failed subprocess will generate a proper descriptive
815 message */
816 bool ExecWait(pid_t Pid,const char *Name,bool Reap)
817 {
818 if (Pid <= 1)
819 return true;
820
821 // Wait and collect the error code
822 int Status;
823 while (waitpid(Pid,&Status,0) != Pid)
824 {
825 if (errno == EINTR)
826 continue;
827
828 if (Reap == true)
829 return false;
830
831 return _error->Error(_("Waited for %s but it wasn't there"),Name);
832 }
833
834
835 // Check for an error code.
836 if (WIFEXITED(Status) == 0 || WEXITSTATUS(Status) != 0)
837 {
838 if (Reap == true)
839 return false;
840 if (WIFSIGNALED(Status) != 0)
841 {
842 if( WTERMSIG(Status) == SIGSEGV)
843 return _error->Error(_("Sub-process %s received a segmentation fault."),Name);
844 else
845 return _error->Error(_("Sub-process %s received signal %u."),Name, WTERMSIG(Status));
846 }
847
848 if (WIFEXITED(Status) != 0)
849 return _error->Error(_("Sub-process %s returned an error code (%u)"),Name,WEXITSTATUS(Status));
850
851 return _error->Error(_("Sub-process %s exited unexpectedly"),Name);
852 }
853
854 return true;
855 }
856 /*}}}*/
857
858 class FileFdPrivate { /*{{{*/
859 public:
860 #ifdef HAVE_ZLIB
861 gzFile gz;
862 #endif
863 #ifdef HAVE_BZ2
864 BZFILE* bz2;
865 #endif
866 #ifdef HAVE_LZMA
867 struct LZMAFILE {
868 FILE* file;
869 uint8_t buffer[4096];
870 lzma_stream stream;
871 lzma_ret err;
872 bool eof;
873 bool compressing;
874
875 LZMAFILE() : file(NULL), eof(false), compressing(false) {}
876 ~LZMAFILE() {
877 if (compressing == true)
878 {
879 for (;;) {
880 stream.avail_out = sizeof(buffer)/sizeof(buffer[0]);
881 stream.next_out = buffer;
882 err = lzma_code(&stream, LZMA_FINISH);
883 if (err != LZMA_OK && err != LZMA_STREAM_END)
884 {
885 _error->Error("~LZMAFILE: Compress finalisation failed");
886 break;
887 }
888 size_t const n = sizeof(buffer)/sizeof(buffer[0]) - stream.avail_out;
889 if (n && fwrite(buffer, 1, n, file) != n)
890 {
891 _error->Errno("~LZMAFILE",_("Write error"));
892 break;
893 }
894 if (err == LZMA_STREAM_END)
895 break;
896 }
897 }
898 lzma_end(&stream);
899 fclose(file);
900 }
901 };
902 LZMAFILE* lzma;
903 #endif
904 int compressed_fd;
905 pid_t compressor_pid;
906 bool pipe;
907 APT::Configuration::Compressor compressor;
908 unsigned int openmode;
909 unsigned long long seekpos;
910 FileFdPrivate() :
911 #ifdef HAVE_ZLIB
912 gz(NULL),
913 #endif
914 #ifdef HAVE_BZ2
915 bz2(NULL),
916 #endif
917 #ifdef HAVE_LZMA
918 lzma(NULL),
919 #endif
920 compressed_fd(-1), compressor_pid(-1), pipe(false),
921 openmode(0), seekpos(0) {};
922 bool InternalClose(std::string const &FileName)
923 {
924 if (false)
925 /* dummy so that the rest can be 'else if's */;
926 #ifdef HAVE_ZLIB
927 else if (gz != NULL) {
928 int const e = gzclose(gz);
929 gz = NULL;
930 // gzdclose() on empty files always fails with "buffer error" here, ignore that
931 if (e != 0 && e != Z_BUF_ERROR)
932 return _error->Errno("close",_("Problem closing the gzip file %s"), FileName.c_str());
933 }
934 #endif
935 #ifdef HAVE_BZ2
936 else if (bz2 != NULL) {
937 BZ2_bzclose(bz2);
938 bz2 = NULL;
939 }
940 #endif
941 #ifdef HAVE_LZMA
942 else if (lzma != NULL) {
943 delete lzma;
944 lzma = NULL;
945 }
946 #endif
947 return true;
948 }
949 bool CloseDown(std::string const &FileName)
950 {
951 bool const Res = InternalClose(FileName);
952
953 if (compressor_pid > 0)
954 ExecWait(compressor_pid, "FileFdCompressor", true);
955 compressor_pid = -1;
956
957 return Res;
958 }
959 bool InternalStream() const {
960 return false
961 #ifdef HAVE_BZ2
962 || bz2 != NULL
963 #endif
964 #ifdef HAVE_LZMA
965 || lzma != NULL
966 #endif
967 ;
968 }
969
970
971 ~FileFdPrivate() { CloseDown(""); }
972 };
973 /*}}}*/
974 // FileFd::Open - Open a file /*{{{*/
975 // ---------------------------------------------------------------------
976 /* The most commonly used open mode combinations are given with Mode */
977 bool FileFd::Open(string FileName,unsigned int const Mode,CompressMode Compress, unsigned long const AccessMode)
978 {
979 if (Mode == ReadOnlyGzip)
980 return Open(FileName, ReadOnly, Gzip, AccessMode);
981
982 if (Compress == Auto && (Mode & WriteOnly) == WriteOnly)
983 return FileFdError("Autodetection on %s only works in ReadOnly openmode!", FileName.c_str());
984
985 std::vector<APT::Configuration::Compressor> const compressors = APT::Configuration::getCompressors();
986 std::vector<APT::Configuration::Compressor>::const_iterator compressor = compressors.begin();
987 if (Compress == Auto)
988 {
989 for (; compressor != compressors.end(); ++compressor)
990 {
991 std::string file = FileName + compressor->Extension;
992 if (FileExists(file) == false)
993 continue;
994 FileName = file;
995 break;
996 }
997 }
998 else if (Compress == Extension)
999 {
1000 std::string::size_type const found = FileName.find_last_of('.');
1001 std::string ext;
1002 if (found != std::string::npos)
1003 {
1004 ext = FileName.substr(found);
1005 if (ext == ".new" || ext == ".bak")
1006 {
1007 std::string::size_type const found2 = FileName.find_last_of('.', found - 1);
1008 if (found2 != std::string::npos)
1009 ext = FileName.substr(found2, found - found2);
1010 else
1011 ext.clear();
1012 }
1013 }
1014 for (; compressor != compressors.end(); ++compressor)
1015 if (ext == compressor->Extension)
1016 break;
1017 // no matching extension - assume uncompressed (imagine files like 'example.org_Packages')
1018 if (compressor == compressors.end())
1019 for (compressor = compressors.begin(); compressor != compressors.end(); ++compressor)
1020 if (compressor->Name == ".")
1021 break;
1022 }
1023 else
1024 {
1025 std::string name;
1026 switch (Compress)
1027 {
1028 case None: name = "."; break;
1029 case Gzip: name = "gzip"; break;
1030 case Bzip2: name = "bzip2"; break;
1031 case Lzma: name = "lzma"; break;
1032 case Xz: name = "xz"; break;
1033 case Auto:
1034 case Extension:
1035 // Unreachable
1036 return FileFdError("Opening File %s in None, Auto or Extension should be already handled?!?", FileName.c_str());
1037 }
1038 for (; compressor != compressors.end(); ++compressor)
1039 if (compressor->Name == name)
1040 break;
1041 if (compressor == compressors.end())
1042 return FileFdError("Can't find a configured compressor %s for file %s", name.c_str(), FileName.c_str());
1043 }
1044
1045 if (compressor == compressors.end())
1046 return FileFdError("Can't find a match for specified compressor mode for file %s", FileName.c_str());
1047 return Open(FileName, Mode, *compressor, AccessMode);
1048 }
1049 bool FileFd::Open(string FileName,unsigned int const Mode,APT::Configuration::Compressor const &compressor, unsigned long const AccessMode)
1050 {
1051 Close();
1052 Flags = AutoClose;
1053
1054 if ((Mode & WriteOnly) != WriteOnly && (Mode & (Atomic | Create | Empty | Exclusive)) != 0)
1055 return FileFdError("ReadOnly mode for %s doesn't accept additional flags!", FileName.c_str());
1056 if ((Mode & ReadWrite) == 0)
1057 return FileFdError("No openmode provided in FileFd::Open for %s", FileName.c_str());
1058
1059 if ((Mode & Atomic) == Atomic)
1060 {
1061 Flags |= Replace;
1062 }
1063 else if ((Mode & (Exclusive | Create)) == (Exclusive | Create))
1064 {
1065 // for atomic, this will be done by rename in Close()
1066 unlink(FileName.c_str());
1067 }
1068 if ((Mode & Empty) == Empty)
1069 {
1070 struct stat Buf;
1071 if (lstat(FileName.c_str(),&Buf) == 0 && S_ISLNK(Buf.st_mode))
1072 unlink(FileName.c_str());
1073 }
1074
1075 int fileflags = 0;
1076 #define if_FLAGGED_SET(FLAG, MODE) if ((Mode & FLAG) == FLAG) fileflags |= MODE
1077 if_FLAGGED_SET(ReadWrite, O_RDWR);
1078 else if_FLAGGED_SET(ReadOnly, O_RDONLY);
1079 else if_FLAGGED_SET(WriteOnly, O_WRONLY);
1080
1081 if_FLAGGED_SET(Create, O_CREAT);
1082 if_FLAGGED_SET(Empty, O_TRUNC);
1083 if_FLAGGED_SET(Exclusive, O_EXCL);
1084 #undef if_FLAGGED_SET
1085
1086 if ((Mode & Atomic) == Atomic)
1087 {
1088 char *name = strdup((FileName + ".XXXXXX").c_str());
1089
1090 if((iFd = mkstemp(name)) == -1)
1091 {
1092 free(name);
1093 return FileFdErrno("mkstemp", "Could not create temporary file for %s", FileName.c_str());
1094 }
1095
1096 TemporaryFileName = string(name);
1097 free(name);
1098
1099 // umask() will always set the umask and return the previous value, so
1100 // we first set the umask and then reset it to the old value
1101 mode_t const CurrentUmask = umask(0);
1102 umask(CurrentUmask);
1103 // calculate the actual file permissions (just like open/creat)
1104 mode_t const FilePermissions = (AccessMode & ~CurrentUmask);
1105
1106 if(fchmod(iFd, FilePermissions) == -1)
1107 return FileFdErrno("fchmod", "Could not change permissions for temporary file %s", TemporaryFileName.c_str());
1108 }
1109 else
1110 iFd = open(FileName.c_str(), fileflags, AccessMode);
1111
1112 this->FileName = FileName;
1113 if (iFd == -1 || OpenInternDescriptor(Mode, compressor) == false)
1114 {
1115 if (iFd != -1)
1116 {
1117 close (iFd);
1118 iFd = -1;
1119 }
1120 return FileFdErrno("open",_("Could not open file %s"), FileName.c_str());
1121 }
1122
1123 SetCloseExec(iFd,true);
1124 return true;
1125 }
1126 /*}}}*/
1127 // FileFd::OpenDescriptor - Open a filedescriptor /*{{{*/
1128 // ---------------------------------------------------------------------
1129 /* */
1130 bool FileFd::OpenDescriptor(int Fd, unsigned int const Mode, CompressMode Compress, bool AutoClose)
1131 {
1132 std::vector<APT::Configuration::Compressor> const compressors = APT::Configuration::getCompressors();
1133 std::vector<APT::Configuration::Compressor>::const_iterator compressor = compressors.begin();
1134 std::string name;
1135
1136 // compat with the old API
1137 if (Mode == ReadOnlyGzip && Compress == None)
1138 Compress = Gzip;
1139
1140 switch (Compress)
1141 {
1142 case None: name = "."; break;
1143 case Gzip: name = "gzip"; break;
1144 case Bzip2: name = "bzip2"; break;
1145 case Lzma: name = "lzma"; break;
1146 case Xz: name = "xz"; break;
1147 case Auto:
1148 case Extension:
1149 if (AutoClose == true && Fd != -1)
1150 close(Fd);
1151 return FileFdError("Opening Fd %d in Auto or Extension compression mode is not supported", Fd);
1152 }
1153 for (; compressor != compressors.end(); ++compressor)
1154 if (compressor->Name == name)
1155 break;
1156 if (compressor == compressors.end())
1157 {
1158 if (AutoClose == true && Fd != -1)
1159 close(Fd);
1160 return FileFdError("Can't find a configured compressor %s for file %s", name.c_str(), FileName.c_str());
1161 }
1162 return OpenDescriptor(Fd, Mode, *compressor, AutoClose);
1163 }
1164 bool FileFd::OpenDescriptor(int Fd, unsigned int const Mode, APT::Configuration::Compressor const &compressor, bool AutoClose)
1165 {
1166 Close();
1167 Flags = (AutoClose) ? FileFd::AutoClose : 0;
1168 iFd = Fd;
1169 this->FileName = "";
1170 if (OpenInternDescriptor(Mode, compressor) == false)
1171 {
1172 if (iFd != -1 && (
1173 (Flags & Compressed) == Compressed ||
1174 AutoClose == true))
1175 {
1176 close (iFd);
1177 iFd = -1;
1178 }
1179 return FileFdError(_("Could not open file descriptor %d"), Fd);
1180 }
1181 return true;
1182 }
1183 bool FileFd::OpenInternDescriptor(unsigned int const Mode, APT::Configuration::Compressor const &compressor)
1184 {
1185 if (iFd == -1)
1186 return false;
1187 if (compressor.Name == "." || compressor.Binary.empty() == true)
1188 return true;
1189
1190 #if defined HAVE_ZLIB || defined HAVE_BZ2 || defined HAVE_LZMA
1191 // the API to open files is similar, so setup to avoid code duplicates later
1192 // and while at it ensure that we close before opening (if its a reopen)
1193 void* (*compress_open)(int, const char *) = NULL;
1194 if (false)
1195 /* dummy so that the rest can be 'else if's */;
1196 #define APT_COMPRESS_INIT(NAME,OPEN) \
1197 else if (compressor.Name == NAME) \
1198 { \
1199 compress_open = (void*(*)(int, const char *)) OPEN; \
1200 if (d != NULL) d->InternalClose(FileName); \
1201 }
1202 #ifdef HAVE_ZLIB
1203 APT_COMPRESS_INIT("gzip", gzdopen)
1204 #endif
1205 #ifdef HAVE_BZ2
1206 APT_COMPRESS_INIT("bzip2", BZ2_bzdopen)
1207 #endif
1208 #ifdef HAVE_LZMA
1209 APT_COMPRESS_INIT("xz", fdopen)
1210 APT_COMPRESS_INIT("lzma", fdopen)
1211 #endif
1212 #undef APT_COMPRESS_INIT
1213 #endif
1214
1215 if (d == NULL)
1216 {
1217 d = new FileFdPrivate();
1218 d->openmode = Mode;
1219 d->compressor = compressor;
1220 #if defined HAVE_ZLIB || defined HAVE_BZ2 || defined HAVE_LZMA
1221 if ((Flags & AutoClose) != AutoClose && compress_open != NULL)
1222 {
1223 // Need to duplicate fd here or gz/bz2 close for cleanup will close the fd as well
1224 int const internFd = dup(iFd);
1225 if (internFd == -1)
1226 return FileFdErrno("OpenInternDescriptor", _("Could not open file descriptor %d"), iFd);
1227 iFd = internFd;
1228 }
1229 #endif
1230 }
1231
1232 #if defined HAVE_ZLIB || defined HAVE_BZ2 || defined HAVE_LZMA
1233 if (compress_open != NULL)
1234 {
1235 void* compress_struct = NULL;
1236 if ((Mode & ReadWrite) == ReadWrite)
1237 compress_struct = compress_open(iFd, "r+");
1238 else if ((Mode & WriteOnly) == WriteOnly)
1239 compress_struct = compress_open(iFd, "w");
1240 else
1241 compress_struct = compress_open(iFd, "r");
1242 if (compress_struct == NULL)
1243 return false;
1244
1245 if (false)
1246 /* dummy so that the rest can be 'else if's */;
1247 #ifdef HAVE_ZLIB
1248 else if (compressor.Name == "gzip")
1249 d->gz = (gzFile) compress_struct;
1250 #endif
1251 #ifdef HAVE_BZ2
1252 else if (compressor.Name == "bzip2")
1253 d->bz2 = (BZFILE*) compress_struct;
1254 #endif
1255 #ifdef HAVE_LZMA
1256 else if (compressor.Name == "xz" || compressor.Name == "lzma")
1257 {
1258 uint32_t const xzlevel = 6;
1259 uint64_t const memlimit = UINT64_MAX;
1260 if (d->lzma == NULL)
1261 d->lzma = new FileFdPrivate::LZMAFILE;
1262 d->lzma->file = (FILE*) compress_struct;
1263 d->lzma->stream = LZMA_STREAM_INIT;
1264
1265 if ((Mode & ReadWrite) == ReadWrite)
1266 return FileFdError("ReadWrite mode is not supported for file %s", FileName.c_str());
1267
1268 if ((Mode & WriteOnly) == WriteOnly)
1269 {
1270 if (compressor.Name == "xz")
1271 {
1272 if (lzma_easy_encoder(&d->lzma->stream, xzlevel, LZMA_CHECK_CRC32) != LZMA_OK)
1273 return false;
1274 }
1275 else
1276 {
1277 lzma_options_lzma options;
1278 lzma_lzma_preset(&options, xzlevel);
1279 if (lzma_alone_encoder(&d->lzma->stream, &options) != LZMA_OK)
1280 return false;
1281 }
1282 d->lzma->compressing = true;
1283 }
1284 else
1285 {
1286 if (compressor.Name == "xz")
1287 {
1288 if (lzma_auto_decoder(&d->lzma->stream, memlimit, 0) != LZMA_OK)
1289 return false;
1290 }
1291 else
1292 {
1293 if (lzma_alone_decoder(&d->lzma->stream, memlimit) != LZMA_OK)
1294 return false;
1295 }
1296 d->lzma->compressing = false;
1297 }
1298 }
1299 #endif
1300 Flags |= Compressed;
1301 return true;
1302 }
1303 #endif
1304
1305 // collect zombies here in case we reopen
1306 if (d->compressor_pid > 0)
1307 ExecWait(d->compressor_pid, "FileFdCompressor", true);
1308
1309 if ((Mode & ReadWrite) == ReadWrite)
1310 return FileFdError("ReadWrite mode is not supported for file %s", FileName.c_str());
1311
1312 bool const Comp = (Mode & WriteOnly) == WriteOnly;
1313 if (Comp == false)
1314 {
1315 // Handle 'decompression' of empty files
1316 struct stat Buf;
1317 fstat(iFd, &Buf);
1318 if (Buf.st_size == 0 && S_ISFIFO(Buf.st_mode) == false)
1319 return true;
1320
1321 // We don't need the file open - instead let the compressor open it
1322 // as he properly knows better how to efficiently read from 'his' file
1323 if (FileName.empty() == false)
1324 {
1325 close(iFd);
1326 iFd = -1;
1327 }
1328 }
1329
1330 // Create a data pipe
1331 int Pipe[2] = {-1,-1};
1332 if (pipe(Pipe) != 0)
1333 return FileFdErrno("pipe",_("Failed to create subprocess IPC"));
1334 for (int J = 0; J != 2; J++)
1335 SetCloseExec(Pipe[J],true);
1336
1337 d->compressed_fd = iFd;
1338 d->pipe = true;
1339
1340 if (Comp == true)
1341 iFd = Pipe[1];
1342 else
1343 iFd = Pipe[0];
1344
1345 // The child..
1346 d->compressor_pid = ExecFork();
1347 if (d->compressor_pid == 0)
1348 {
1349 if (Comp == true)
1350 {
1351 dup2(d->compressed_fd,STDOUT_FILENO);
1352 dup2(Pipe[0],STDIN_FILENO);
1353 }
1354 else
1355 {
1356 if (d->compressed_fd != -1)
1357 dup2(d->compressed_fd,STDIN_FILENO);
1358 dup2(Pipe[1],STDOUT_FILENO);
1359 }
1360 int const nullfd = open("/dev/null", O_WRONLY);
1361 if (nullfd != -1)
1362 {
1363 dup2(nullfd,STDERR_FILENO);
1364 close(nullfd);
1365 }
1366
1367 SetCloseExec(STDOUT_FILENO,false);
1368 SetCloseExec(STDIN_FILENO,false);
1369
1370 std::vector<char const*> Args;
1371 Args.push_back(compressor.Binary.c_str());
1372 std::vector<std::string> const * const addArgs =
1373 (Comp == true) ? &(compressor.CompressArgs) : &(compressor.UncompressArgs);
1374 for (std::vector<std::string>::const_iterator a = addArgs->begin();
1375 a != addArgs->end(); ++a)
1376 Args.push_back(a->c_str());
1377 if (Comp == false && FileName.empty() == false)
1378 {
1379 // commands not needing arguments, do not need to be told about using standard output
1380 // in reality, only testcases with tools like cat, rev, rot13, … are able to trigger this
1381 if (compressor.CompressArgs.empty() == false && compressor.UncompressArgs.empty() == false)
1382 Args.push_back("--stdout");
1383 if (TemporaryFileName.empty() == false)
1384 Args.push_back(TemporaryFileName.c_str());
1385 else
1386 Args.push_back(FileName.c_str());
1387 }
1388 Args.push_back(NULL);
1389
1390 execvp(Args[0],(char **)&Args[0]);
1391 cerr << _("Failed to exec compressor ") << Args[0] << endl;
1392 _exit(100);
1393 }
1394 if (Comp == true)
1395 close(Pipe[0]);
1396 else
1397 close(Pipe[1]);
1398
1399 return true;
1400 }
1401 /*}}}*/
1402 // FileFd::~File - Closes the file /*{{{*/
1403 // ---------------------------------------------------------------------
1404 /* If the proper modes are selected then we close the Fd and possibly
1405 unlink the file on error. */
1406 FileFd::~FileFd()
1407 {
1408 Close();
1409 if (d != NULL)
1410 d->CloseDown(FileName);
1411 delete d;
1412 d = NULL;
1413 }
1414 /*}}}*/
1415 // FileFd::Read - Read a bit of the file /*{{{*/
1416 // ---------------------------------------------------------------------
1417 /* We are careful to handle interruption by a signal while reading
1418 gracefully. */
1419 bool FileFd::Read(void *To,unsigned long long Size,unsigned long long *Actual)
1420 {
1421 ssize_t Res;
1422 errno = 0;
1423 if (Actual != 0)
1424 *Actual = 0;
1425 *((char *)To) = '\0';
1426 do
1427 {
1428 if (false)
1429 /* dummy so that the rest can be 'else if's */;
1430 #ifdef HAVE_ZLIB
1431 else if (d != NULL && d->gz != NULL)
1432 Res = gzread(d->gz,To,Size);
1433 #endif
1434 #ifdef HAVE_BZ2
1435 else if (d != NULL && d->bz2 != NULL)
1436 Res = BZ2_bzread(d->bz2,To,Size);
1437 #endif
1438 #ifdef HAVE_LZMA
1439 else if (d != NULL && d->lzma != NULL)
1440 {
1441 if (d->lzma->eof == true)
1442 break;
1443
1444 d->lzma->stream.next_out = (uint8_t *) To;
1445 d->lzma->stream.avail_out = Size;
1446 if (d->lzma->stream.avail_in == 0)
1447 {
1448 d->lzma->stream.next_in = d->lzma->buffer;
1449 d->lzma->stream.avail_in = fread(d->lzma->buffer, 1, sizeof(d->lzma->buffer)/sizeof(d->lzma->buffer[0]), d->lzma->file);
1450 }
1451 d->lzma->err = lzma_code(&d->lzma->stream, LZMA_RUN);
1452 if (d->lzma->err == LZMA_STREAM_END)
1453 {
1454 d->lzma->eof = true;
1455 Res = Size - d->lzma->stream.avail_out;
1456 }
1457 else if (d->lzma->err != LZMA_OK)
1458 {
1459 Res = -1;
1460 errno = 0;
1461 }
1462 else
1463 {
1464 Res = Size - d->lzma->stream.avail_out;
1465 if (Res == 0)
1466 {
1467 // lzma run was okay, but produced no output…
1468 Res = -1;
1469 errno = EINTR;
1470 }
1471 }
1472 }
1473 #endif
1474 else
1475 Res = read(iFd,To,Size);
1476
1477 if (Res < 0)
1478 {
1479 if (errno == EINTR)
1480 {
1481 // trick the while-loop into running again
1482 Res = 1;
1483 errno = 0;
1484 continue;
1485 }
1486 if (false)
1487 /* dummy so that the rest can be 'else if's */;
1488 #ifdef HAVE_ZLIB
1489 else if (d != NULL && d->gz != NULL)
1490 {
1491 int err;
1492 char const * const errmsg = gzerror(d->gz, &err);
1493 if (err != Z_ERRNO)
1494 return FileFdError("gzread: %s (%d: %s)", _("Read error"), err, errmsg);
1495 }
1496 #endif
1497 #ifdef HAVE_BZ2
1498 else if (d != NULL && d->bz2 != NULL)
1499 {
1500 int err;
1501 char const * const errmsg = BZ2_bzerror(d->bz2, &err);
1502 if (err != BZ_IO_ERROR)
1503 return FileFdError("BZ2_bzread: %s (%d: %s)", _("Read error"), err, errmsg);
1504 }
1505 #endif
1506 #ifdef HAVE_LZMA
1507 else if (d != NULL && d->lzma != NULL)
1508 return FileFdError("lzma_read: %s (%d)", _("Read error"), d->lzma->err);
1509 #endif
1510 return FileFdErrno("read",_("Read error"));
1511 }
1512
1513 To = (char *)To + Res;
1514 Size -= Res;
1515 if (d != NULL)
1516 d->seekpos += Res;
1517 if (Actual != 0)
1518 *Actual += Res;
1519 }
1520 while (Res > 0 && Size > 0);
1521
1522 if (Size == 0)
1523 return true;
1524
1525 // Eof handling
1526 if (Actual != 0)
1527 {
1528 Flags |= HitEof;
1529 return true;
1530 }
1531
1532 return FileFdError(_("read, still have %llu to read but none left"), Size);
1533 }
1534 /*}}}*/
1535 // FileFd::ReadLine - Read a complete line from the file /*{{{*/
1536 // ---------------------------------------------------------------------
1537 /* Beware: This method can be quiet slow for big buffers on UNcompressed
1538 files because of the naive implementation! */
1539 char* FileFd::ReadLine(char *To, unsigned long long const Size)
1540 {
1541 *To = '\0';
1542 #ifdef HAVE_ZLIB
1543 if (d != NULL && d->gz != NULL)
1544 return gzgets(d->gz, To, Size);
1545 #endif
1546
1547 unsigned long long read = 0;
1548 while ((Size - 1) != read)
1549 {
1550 unsigned long long done = 0;
1551 if (Read(To + read, 1, &done) == false)
1552 return NULL;
1553 if (done == 0)
1554 break;
1555 if (To[read++] == '\n')
1556 break;
1557 }
1558 if (read == 0)
1559 return NULL;
1560 To[read] = '\0';
1561 return To;
1562 }
1563 /*}}}*/
1564 // FileFd::Write - Write to the file /*{{{*/
1565 // ---------------------------------------------------------------------
1566 /* */
1567 bool FileFd::Write(const void *From,unsigned long long Size)
1568 {
1569 ssize_t Res;
1570 errno = 0;
1571 do
1572 {
1573 if (false)
1574 /* dummy so that the rest can be 'else if's */;
1575 #ifdef HAVE_ZLIB
1576 else if (d != NULL && d->gz != NULL)
1577 Res = gzwrite(d->gz,From,Size);
1578 #endif
1579 #ifdef HAVE_BZ2
1580 else if (d != NULL && d->bz2 != NULL)
1581 Res = BZ2_bzwrite(d->bz2,(void*)From,Size);
1582 #endif
1583 #ifdef HAVE_LZMA
1584 else if (d != NULL && d->lzma != NULL)
1585 {
1586 d->lzma->stream.next_in = (uint8_t *)From;
1587 d->lzma->stream.avail_in = Size;
1588 d->lzma->stream.next_out = d->lzma->buffer;
1589 d->lzma->stream.avail_out = sizeof(d->lzma->buffer)/sizeof(d->lzma->buffer[0]);
1590 d->lzma->err = lzma_code(&d->lzma->stream, LZMA_RUN);
1591 if (d->lzma->err != LZMA_OK)
1592 return false;
1593 size_t const n = sizeof(d->lzma->buffer)/sizeof(d->lzma->buffer[0]) - d->lzma->stream.avail_out;
1594 size_t const m = (n == 0) ? 0 : fwrite(d->lzma->buffer, 1, n, d->lzma->file);
1595 if (m != n)
1596 Res = -1;
1597 else
1598 Res = Size - d->lzma->stream.avail_in;
1599 }
1600 #endif
1601 else
1602 Res = write(iFd,From,Size);
1603
1604 if (Res < 0 && errno == EINTR)
1605 continue;
1606 if (Res < 0)
1607 {
1608 if (false)
1609 /* dummy so that the rest can be 'else if's */;
1610 #ifdef HAVE_ZLIB
1611 else if (d != NULL && d->gz != NULL)
1612 {
1613 int err;
1614 char const * const errmsg = gzerror(d->gz, &err);
1615 if (err != Z_ERRNO)
1616 return FileFdError("gzwrite: %s (%d: %s)", _("Write error"), err, errmsg);
1617 }
1618 #endif
1619 #ifdef HAVE_BZ2
1620 else if (d != NULL && d->bz2 != NULL)
1621 {
1622 int err;
1623 char const * const errmsg = BZ2_bzerror(d->bz2, &err);
1624 if (err != BZ_IO_ERROR)
1625 return FileFdError("BZ2_bzwrite: %s (%d: %s)", _("Write error"), err, errmsg);
1626 }
1627 #endif
1628 #ifdef HAVE_LZMA
1629 else if (d != NULL && d->lzma != NULL)
1630 return FileFdErrno("lzma_fwrite", _("Write error"));
1631 #endif
1632 return FileFdErrno("write",_("Write error"));
1633 }
1634
1635 From = (char const *)From + Res;
1636 Size -= Res;
1637 if (d != NULL)
1638 d->seekpos += Res;
1639 }
1640 while (Res > 0 && Size > 0);
1641
1642 if (Size == 0)
1643 return true;
1644
1645 return FileFdError(_("write, still have %llu to write but couldn't"), Size);
1646 }
1647 bool FileFd::Write(int Fd, const void *From, unsigned long long Size)
1648 {
1649 ssize_t Res;
1650 errno = 0;
1651 do
1652 {
1653 Res = write(Fd,From,Size);
1654 if (Res < 0 && errno == EINTR)
1655 continue;
1656 if (Res < 0)
1657 return _error->Errno("write",_("Write error"));
1658
1659 From = (char const *)From + Res;
1660 Size -= Res;
1661 }
1662 while (Res > 0 && Size > 0);
1663
1664 if (Size == 0)
1665 return true;
1666
1667 return _error->Error(_("write, still have %llu to write but couldn't"), Size);
1668 }
1669 /*}}}*/
1670 // FileFd::Seek - Seek in the file /*{{{*/
1671 // ---------------------------------------------------------------------
1672 /* */
1673 bool FileFd::Seek(unsigned long long To)
1674 {
1675 Flags &= ~HitEof;
1676
1677 if (d != NULL && (d->pipe == true || d->InternalStream() == true))
1678 {
1679 // Our poor man seeking in pipes is costly, so try to avoid it
1680 unsigned long long seekpos = Tell();
1681 if (seekpos == To)
1682 return true;
1683 else if (seekpos < To)
1684 return Skip(To - seekpos);
1685
1686 if ((d->openmode & ReadOnly) != ReadOnly)
1687 return FileFdError("Reopen is only implemented for read-only files!");
1688 d->InternalClose(FileName);
1689 if (iFd != -1)
1690 close(iFd);
1691 iFd = -1;
1692 if (TemporaryFileName.empty() == false)
1693 iFd = open(TemporaryFileName.c_str(), O_RDONLY);
1694 else if (FileName.empty() == false)
1695 iFd = open(FileName.c_str(), O_RDONLY);
1696 else
1697 {
1698 if (d->compressed_fd > 0)
1699 if (lseek(d->compressed_fd, 0, SEEK_SET) != 0)
1700 iFd = d->compressed_fd;
1701 if (iFd < 0)
1702 return FileFdError("Reopen is not implemented for pipes opened with FileFd::OpenDescriptor()!");
1703 }
1704
1705 if (OpenInternDescriptor(d->openmode, d->compressor) == false)
1706 return FileFdError("Seek on file %s because it couldn't be reopened", FileName.c_str());
1707
1708 if (To != 0)
1709 return Skip(To);
1710
1711 d->seekpos = To;
1712 return true;
1713 }
1714 off_t res;
1715 #ifdef HAVE_ZLIB
1716 if (d != NULL && d->gz)
1717 res = gzseek(d->gz,To,SEEK_SET);
1718 else
1719 #endif
1720 res = lseek(iFd,To,SEEK_SET);
1721 if (res != (off_t)To)
1722 return FileFdError("Unable to seek to %llu", To);
1723
1724 if (d != NULL)
1725 d->seekpos = To;
1726 return true;
1727 }
1728 /*}}}*/
1729 // FileFd::Skip - Seek in the file /*{{{*/
1730 // ---------------------------------------------------------------------
1731 /* */
1732 bool FileFd::Skip(unsigned long long Over)
1733 {
1734 if (d != NULL && (d->pipe == true || d->InternalStream() == true))
1735 {
1736 char buffer[1024];
1737 while (Over != 0)
1738 {
1739 unsigned long long toread = std::min((unsigned long long) sizeof(buffer), Over);
1740 if (Read(buffer, toread) == false)
1741 return FileFdError("Unable to seek ahead %llu",Over);
1742 Over -= toread;
1743 }
1744 return true;
1745 }
1746
1747 off_t res;
1748 #ifdef HAVE_ZLIB
1749 if (d != NULL && d->gz != NULL)
1750 res = gzseek(d->gz,Over,SEEK_CUR);
1751 else
1752 #endif
1753 res = lseek(iFd,Over,SEEK_CUR);
1754 if (res < 0)
1755 return FileFdError("Unable to seek ahead %llu",Over);
1756 if (d != NULL)
1757 d->seekpos = res;
1758
1759 return true;
1760 }
1761 /*}}}*/
1762 // FileFd::Truncate - Truncate the file /*{{{*/
1763 // ---------------------------------------------------------------------
1764 /* */
1765 bool FileFd::Truncate(unsigned long long To)
1766 {
1767 // truncating /dev/null is always successful - as we get an error otherwise
1768 if (To == 0 && FileName == "/dev/null")
1769 return true;
1770 #if defined HAVE_ZLIB || defined HAVE_BZ2 || defined HAVE_LZMA
1771 if (d != NULL && (d->InternalStream() == true
1772 #ifdef HAVE_ZLIB
1773 || d->gz != NULL
1774 #endif
1775 ))
1776 return FileFdError("Truncating compressed files is not implemented (%s)", FileName.c_str());
1777 #endif
1778 if (ftruncate(iFd,To) != 0)
1779 return FileFdError("Unable to truncate to %llu",To);
1780
1781 return true;
1782 }
1783 /*}}}*/
1784 // FileFd::Tell - Current seek position /*{{{*/
1785 // ---------------------------------------------------------------------
1786 /* */
1787 unsigned long long FileFd::Tell()
1788 {
1789 // In theory, we could just return seekpos here always instead of
1790 // seeking around, but not all users of FileFd use always Seek() and co
1791 // so d->seekpos isn't always true and we can just use it as a hint if
1792 // we have nothing else, but not always as an authority…
1793 if (d != NULL && (d->pipe == true || d->InternalStream() == true))
1794 return d->seekpos;
1795
1796 off_t Res;
1797 #ifdef HAVE_ZLIB
1798 if (d != NULL && d->gz != NULL)
1799 Res = gztell(d->gz);
1800 else
1801 #endif
1802 Res = lseek(iFd,0,SEEK_CUR);
1803 if (Res == (off_t)-1)
1804 FileFdErrno("lseek","Failed to determine the current file position");
1805 if (d != NULL)
1806 d->seekpos = Res;
1807 return Res;
1808 }
1809 /*}}}*/
1810 static bool StatFileFd(char const * const msg, int const iFd, std::string const &FileName, struct stat &Buf, FileFdPrivate * const d) /*{{{*/
1811 {
1812 bool ispipe = (d != NULL && d->pipe == true);
1813 if (ispipe == false)
1814 {
1815 if (fstat(iFd,&Buf) != 0)
1816 // higher-level code will generate more meaningful messages,
1817 // even translated this would be meaningless for users
1818 return _error->Errno("fstat", "Unable to determine %s for fd %i", msg, iFd);
1819 ispipe = S_ISFIFO(Buf.st_mode);
1820 }
1821
1822 // for compressor pipes st_size is undefined and at 'best' zero
1823 if (ispipe == true)
1824 {
1825 // we set it here, too, as we get the info here for free
1826 // in theory the Open-methods should take care of it already
1827 if (d != NULL)
1828 d->pipe = true;
1829 if (stat(FileName.c_str(), &Buf) != 0)
1830 return _error->Errno("fstat", "Unable to determine %s for file %s", msg, FileName.c_str());
1831 }
1832 return true;
1833 }
1834 /*}}}*/
1835 // FileFd::FileSize - Return the size of the file /*{{{*/
1836 unsigned long long FileFd::FileSize()
1837 {
1838 struct stat Buf;
1839 if (StatFileFd("file size", iFd, FileName, Buf, d) == false)
1840 {
1841 Flags |= Fail;
1842 return 0;
1843 }
1844 return Buf.st_size;
1845 }
1846 /*}}}*/
1847 // FileFd::ModificationTime - Return the time of last touch /*{{{*/
1848 time_t FileFd::ModificationTime()
1849 {
1850 struct stat Buf;
1851 if (StatFileFd("modification time", iFd, FileName, Buf, d) == false)
1852 {
1853 Flags |= Fail;
1854 return 0;
1855 }
1856 return Buf.st_mtime;
1857 }
1858 /*}}}*/
1859 // FileFd::Size - Return the size of the content in the file /*{{{*/
1860 // ---------------------------------------------------------------------
1861 /* */
1862 unsigned long long FileFd::Size()
1863 {
1864 unsigned long long size = FileSize();
1865
1866 // for compressor pipes st_size is undefined and at 'best' zero,
1867 // so we 'read' the content and 'seek' back - see there
1868 if (d != NULL && (d->pipe == true || (d->InternalStream() == true && size > 0)))
1869 {
1870 unsigned long long const oldSeek = Tell();
1871 char ignore[1000];
1872 unsigned long long read = 0;
1873 do {
1874 if (Read(ignore, sizeof(ignore), &read) == false)
1875 {
1876 Seek(oldSeek);
1877 return 0;
1878 }
1879 } while(read != 0);
1880 size = Tell();
1881 Seek(oldSeek);
1882 }
1883 #ifdef HAVE_ZLIB
1884 // only check gzsize if we are actually a gzip file, just checking for
1885 // "gz" is not sufficient as uncompressed files could be opened with
1886 // gzopen in "direct" mode as well
1887 else if (d != NULL && d->gz && !gzdirect(d->gz) && size > 0)
1888 {
1889 off_t const oldPos = lseek(iFd,0,SEEK_CUR);
1890 /* unfortunately zlib.h doesn't provide a gzsize(), so we have to do
1891 * this ourselves; the original (uncompressed) file size is the last 32
1892 * bits of the file */
1893 // FIXME: Size for gz-files is limited by 32bit… no largefile support
1894 if (lseek(iFd, -4, SEEK_END) < 0)
1895 {
1896 FileFdErrno("lseek","Unable to seek to end of gzipped file");
1897 return 0;
1898 }
1899 size = 0;
1900 if (read(iFd, &size, 4) != 4)
1901 {
1902 FileFdErrno("read","Unable to read original size of gzipped file");
1903 return 0;
1904 }
1905
1906 #ifdef WORDS_BIGENDIAN
1907 uint32_t tmp_size = size;
1908 uint8_t const * const p = (uint8_t const * const) &tmp_size;
1909 tmp_size = (p[3] << 24) | (p[2] << 16) | (p[1] << 8) | p[0];
1910 size = tmp_size;
1911 #endif
1912
1913 if (lseek(iFd, oldPos, SEEK_SET) < 0)
1914 {
1915 FileFdErrno("lseek","Unable to seek in gzipped file");
1916 return 0;
1917 }
1918
1919 return size;
1920 }
1921 #endif
1922
1923 return size;
1924 }
1925 /*}}}*/
1926 // FileFd::Close - Close the file if the close flag is set /*{{{*/
1927 // ---------------------------------------------------------------------
1928 /* */
1929 bool FileFd::Close()
1930 {
1931 if (iFd == -1)
1932 return true;
1933
1934 bool Res = true;
1935 if ((Flags & AutoClose) == AutoClose)
1936 {
1937 if ((Flags & Compressed) != Compressed && iFd > 0 && close(iFd) != 0)
1938 Res &= _error->Errno("close",_("Problem closing the file %s"), FileName.c_str());
1939 if (d != NULL)
1940 {
1941 Res &= d->CloseDown(FileName);
1942 delete d;
1943 d = NULL;
1944 }
1945 }
1946
1947 if ((Flags & Replace) == Replace) {
1948 if (rename(TemporaryFileName.c_str(), FileName.c_str()) != 0)
1949 Res &= _error->Errno("rename",_("Problem renaming the file %s to %s"), TemporaryFileName.c_str(), FileName.c_str());
1950
1951 FileName = TemporaryFileName; // for the unlink() below.
1952 TemporaryFileName.clear();
1953 }
1954
1955 iFd = -1;
1956
1957 if ((Flags & Fail) == Fail && (Flags & DelOnFail) == DelOnFail &&
1958 FileName.empty() == false)
1959 if (unlink(FileName.c_str()) != 0)
1960 Res &= _error->WarningE("unlnk",_("Problem unlinking the file %s"), FileName.c_str());
1961
1962 if (Res == false)
1963 Flags |= Fail;
1964 return Res;
1965 }
1966 /*}}}*/
1967 // FileFd::Sync - Sync the file /*{{{*/
1968 // ---------------------------------------------------------------------
1969 /* */
1970 bool FileFd::Sync()
1971 {
1972 if (fsync(iFd) != 0)
1973 return FileFdErrno("sync",_("Problem syncing the file"));
1974 return true;
1975 }
1976 /*}}}*/
1977 // FileFd::FileFdErrno - set Fail and call _error->Errno *{{{*/
1978 bool FileFd::FileFdErrno(const char *Function, const char *Description,...)
1979 {
1980 Flags |= Fail;
1981 va_list args;
1982 size_t msgSize = 400;
1983 int const errsv = errno;
1984 while (true)
1985 {
1986 va_start(args,Description);
1987 if (_error->InsertErrno(GlobalError::ERROR, Function, Description, args, errsv, msgSize) == false)
1988 break;
1989 va_end(args);
1990 }
1991 return false;
1992 }
1993 /*}}}*/
1994 // FileFd::FileFdError - set Fail and call _error->Error *{{{*/
1995 bool FileFd::FileFdError(const char *Description,...) {
1996 Flags |= Fail;
1997 va_list args;
1998 size_t msgSize = 400;
1999 while (true)
2000 {
2001 va_start(args,Description);
2002 if (_error->Insert(GlobalError::ERROR, Description, args, msgSize) == false)
2003 break;
2004 va_end(args);
2005 }
2006 return false;
2007 }
2008 /*}}}*/
2009
2010 APT_DEPRECATED gzFile FileFd::gzFd() {
2011 #ifdef HAVE_ZLIB
2012 return d->gz;
2013 #else
2014 return NULL;
2015 #endif
2016 }
2017
2018
2019 // Glob - wrapper around "glob()" /*{{{*/
2020 // ---------------------------------------------------------------------
2021 /* */
2022 std::vector<std::string> Glob(std::string const &pattern, int flags)
2023 {
2024 std::vector<std::string> result;
2025 glob_t globbuf;
2026 int glob_res;
2027 unsigned int i;
2028
2029 glob_res = glob(pattern.c_str(), flags, NULL, &globbuf);
2030
2031 if (glob_res != 0)
2032 {
2033 if(glob_res != GLOB_NOMATCH) {
2034 _error->Errno("glob", "Problem with glob");
2035 return result;
2036 }
2037 }
2038
2039 // append results
2040 for(i=0;i<globbuf.gl_pathc;i++)
2041 result.push_back(string(globbuf.gl_pathv[i]));
2042
2043 globfree(&globbuf);
2044 return result;
2045 }
2046 /*}}}*/
2047
2048 std::string GetTempDir()
2049 {
2050 const char *tmpdir = getenv("TMPDIR");
2051
2052 #ifdef P_tmpdir
2053 if (!tmpdir)
2054 tmpdir = P_tmpdir;
2055 #endif
2056
2057 // check that tmpdir is set and exists
2058 struct stat st;
2059 if (!tmpdir || strlen(tmpdir) == 0 || stat(tmpdir, &st) != 0)
2060 tmpdir = "/tmp";
2061
2062 return string(tmpdir);
2063 }
2064
2065 FileFd* GetTempFile(std::string const &Prefix, bool ImmediateUnlink)
2066 {
2067 char fn[512];
2068 FileFd *Fd = new FileFd();
2069
2070 std::string tempdir = GetTempDir();
2071 snprintf(fn, sizeof(fn), "%s/%s.XXXXXX",
2072 tempdir.c_str(), Prefix.c_str());
2073 int fd = mkstemp(fn);
2074 if(ImmediateUnlink)
2075 unlink(fn);
2076 if (fd < 0)
2077 {
2078 _error->Errno("GetTempFile",_("Unable to mkstemp %s"), fn);
2079 return NULL;
2080 }
2081 if (!Fd->OpenDescriptor(fd, FileFd::WriteOnly, FileFd::None, true))
2082 {
2083 _error->Errno("GetTempFile",_("Unable to write to %s"),fn);
2084 return NULL;
2085 }
2086
2087 return Fd;
2088 }
2089
2090 bool Rename(std::string From, std::string To)
2091 {
2092 if (rename(From.c_str(),To.c_str()) != 0)
2093 {
2094 _error->Error(_("rename failed, %s (%s -> %s)."),strerror(errno),
2095 From.c_str(),To.c_str());
2096 return false;
2097 }
2098 return true;
2099 }
2100
2101 bool Popen(const char* Args[], FileFd &Fd, pid_t &Child, FileFd::OpenMode Mode)
2102 {
2103 int fd;
2104 if (Mode != FileFd::ReadOnly && Mode != FileFd::WriteOnly)
2105 return _error->Error("Popen supports ReadOnly (x)or WriteOnly mode only");
2106
2107 int Pipe[2] = {-1, -1};
2108 if(pipe(Pipe) != 0)
2109 {
2110 return _error->Errno("pipe", _("Failed to create subprocess IPC"));
2111 return NULL;
2112 }
2113 std::set<int> keep_fds;
2114 keep_fds.insert(Pipe[0]);
2115 keep_fds.insert(Pipe[1]);
2116 Child = ExecFork(keep_fds);
2117 if(Child < 0)
2118 return _error->Errno("fork", "Failed to fork");
2119 if(Child == 0)
2120 {
2121 if(Mode == FileFd::ReadOnly)
2122 {
2123 close(Pipe[0]);
2124 fd = Pipe[1];
2125 }
2126 else if(Mode == FileFd::WriteOnly)
2127 {
2128 close(Pipe[1]);
2129 fd = Pipe[0];
2130 }
2131
2132 if(Mode == FileFd::ReadOnly)
2133 {
2134 dup2(fd, 1);
2135 dup2(fd, 2);
2136 } else if(Mode == FileFd::WriteOnly)
2137 dup2(fd, 0);
2138
2139 execv(Args[0], (char**)Args);
2140 _exit(100);
2141 }
2142 if(Mode == FileFd::ReadOnly)
2143 {
2144 close(Pipe[1]);
2145 fd = Pipe[0];
2146 } else if(Mode == FileFd::WriteOnly)
2147 {
2148 close(Pipe[0]);
2149 fd = Pipe[1];
2150 }
2151 Fd.OpenDescriptor(fd, Mode, FileFd::None, true);
2152
2153 return true;
2154 }