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