]> git.saurik.com Git - apt.git/blob - apt-pkg/contrib/fileutl.cc
Merge remote-tracking branch 'mvo/feature/deb-src-bts731102' into debian/sid
[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 // MergeKeepFdsFromConfiguration - Merge APT::Keep-Fds configuration /*{{{*/
764 // ---------------------------------------------------------------------
765 /* This is used to merge the APT::Keep-Fds with the provided KeepFDs
766 * set.
767 */
768 void MergeKeepFdsFromConfiguration(std::set<int> &KeepFDs)
769 {
770 Configuration::Item const *Opts = _config->Tree("APT::Keep-Fds");
771 if (Opts != 0 && Opts->Child != 0)
772 {
773 Opts = Opts->Child;
774 for (; Opts != 0; Opts = Opts->Next)
775 {
776 if (Opts->Value.empty() == true)
777 continue;
778 int fd = atoi(Opts->Value.c_str());
779 KeepFDs.insert(fd);
780 }
781 }
782 }
783 /*}}}*/
784 // ExecFork - Magical fork that sanitizes the context before execing /*{{{*/
785 // ---------------------------------------------------------------------
786 /* This is used if you want to cleanse the environment for the forked
787 child, it fixes up the important signals and nukes all of the fds,
788 otherwise acts like normal fork. */
789 pid_t ExecFork()
790 {
791 set<int> KeepFDs;
792 // we need to merge the Keep-Fds as external tools like
793 // debconf-apt-progress use it
794 MergeKeepFdsFromConfiguration(KeepFDs);
795 return ExecFork(KeepFDs);
796 }
797
798 pid_t ExecFork(std::set<int> KeepFDs)
799 {
800 // Fork off the process
801 pid_t Process = fork();
802 if (Process < 0)
803 {
804 cerr << "FATAL -> Failed to fork." << endl;
805 exit(100);
806 }
807
808 // Spawn the subprocess
809 if (Process == 0)
810 {
811 // Setup the signals
812 signal(SIGPIPE,SIG_DFL);
813 signal(SIGQUIT,SIG_DFL);
814 signal(SIGINT,SIG_DFL);
815 signal(SIGWINCH,SIG_DFL);
816 signal(SIGCONT,SIG_DFL);
817 signal(SIGTSTP,SIG_DFL);
818
819 // Close all of our FDs - just in case
820 for (int K = 3; K != sysconf(_SC_OPEN_MAX); K++)
821 {
822 if(KeepFDs.find(K) == KeepFDs.end())
823 fcntl(K,F_SETFD,FD_CLOEXEC);
824 }
825 }
826
827 return Process;
828 }
829 /*}}}*/
830 // ExecWait - Fancy waitpid /*{{{*/
831 // ---------------------------------------------------------------------
832 /* Waits for the given sub process. If Reap is set then no errors are
833 generated. Otherwise a failed subprocess will generate a proper descriptive
834 message */
835 bool ExecWait(pid_t Pid,const char *Name,bool Reap)
836 {
837 if (Pid <= 1)
838 return true;
839
840 // Wait and collect the error code
841 int Status;
842 while (waitpid(Pid,&Status,0) != Pid)
843 {
844 if (errno == EINTR)
845 continue;
846
847 if (Reap == true)
848 return false;
849
850 return _error->Error(_("Waited for %s but it wasn't there"),Name);
851 }
852
853
854 // Check for an error code.
855 if (WIFEXITED(Status) == 0 || WEXITSTATUS(Status) != 0)
856 {
857 if (Reap == true)
858 return false;
859 if (WIFSIGNALED(Status) != 0)
860 {
861 if( WTERMSIG(Status) == SIGSEGV)
862 return _error->Error(_("Sub-process %s received a segmentation fault."),Name);
863 else
864 return _error->Error(_("Sub-process %s received signal %u."),Name, WTERMSIG(Status));
865 }
866
867 if (WIFEXITED(Status) != 0)
868 return _error->Error(_("Sub-process %s returned an error code (%u)"),Name,WEXITSTATUS(Status));
869
870 return _error->Error(_("Sub-process %s exited unexpectedly"),Name);
871 }
872
873 return true;
874 }
875 /*}}}*/
876
877 // FileFd::Open - Open a file /*{{{*/
878 // ---------------------------------------------------------------------
879 /* The most commonly used open mode combinations are given with Mode */
880 bool FileFd::Open(string FileName,unsigned int const Mode,CompressMode Compress, unsigned long const Perms)
881 {
882 if (Mode == ReadOnlyGzip)
883 return Open(FileName, ReadOnly, Gzip, Perms);
884
885 if (Compress == Auto && (Mode & WriteOnly) == WriteOnly)
886 return FileFdError("Autodetection on %s only works in ReadOnly openmode!", FileName.c_str());
887
888 std::vector<APT::Configuration::Compressor> const compressors = APT::Configuration::getCompressors();
889 std::vector<APT::Configuration::Compressor>::const_iterator compressor = compressors.begin();
890 if (Compress == Auto)
891 {
892 for (; compressor != compressors.end(); ++compressor)
893 {
894 std::string file = std::string(FileName).append(compressor->Extension);
895 if (FileExists(file) == false)
896 continue;
897 FileName = file;
898 break;
899 }
900 }
901 else if (Compress == Extension)
902 {
903 std::string::size_type const found = FileName.find_last_of('.');
904 std::string ext;
905 if (found != std::string::npos)
906 {
907 ext = FileName.substr(found);
908 if (ext == ".new" || ext == ".bak")
909 {
910 std::string::size_type const found2 = FileName.find_last_of('.', found - 1);
911 if (found2 != std::string::npos)
912 ext = FileName.substr(found2, found - found2);
913 else
914 ext.clear();
915 }
916 }
917 for (; compressor != compressors.end(); ++compressor)
918 if (ext == compressor->Extension)
919 break;
920 // no matching extension - assume uncompressed (imagine files like 'example.org_Packages')
921 if (compressor == compressors.end())
922 for (compressor = compressors.begin(); compressor != compressors.end(); ++compressor)
923 if (compressor->Name == ".")
924 break;
925 }
926 else
927 {
928 std::string name;
929 switch (Compress)
930 {
931 case None: name = "."; break;
932 case Gzip: name = "gzip"; break;
933 case Bzip2: name = "bzip2"; break;
934 case Lzma: name = "lzma"; break;
935 case Xz: name = "xz"; break;
936 case Auto:
937 case Extension:
938 // Unreachable
939 return FileFdError("Opening File %s in None, Auto or Extension should be already handled?!?", FileName.c_str());
940 }
941 for (; compressor != compressors.end(); ++compressor)
942 if (compressor->Name == name)
943 break;
944 if (compressor == compressors.end())
945 return FileFdError("Can't find a configured compressor %s for file %s", name.c_str(), FileName.c_str());
946 }
947
948 if (compressor == compressors.end())
949 return FileFdError("Can't find a match for specified compressor mode for file %s", FileName.c_str());
950 return Open(FileName, Mode, *compressor, Perms);
951 }
952 bool FileFd::Open(string FileName,unsigned int const Mode,APT::Configuration::Compressor const &compressor, unsigned long const Perms)
953 {
954 Close();
955 Flags = AutoClose;
956
957 if ((Mode & WriteOnly) != WriteOnly && (Mode & (Atomic | Create | Empty | Exclusive)) != 0)
958 return FileFdError("ReadOnly mode for %s doesn't accept additional flags!", FileName.c_str());
959 if ((Mode & ReadWrite) == 0)
960 return FileFdError("No openmode provided in FileFd::Open for %s", FileName.c_str());
961
962 if ((Mode & Atomic) == Atomic)
963 {
964 Flags |= Replace;
965 }
966 else if ((Mode & (Exclusive | Create)) == (Exclusive | Create))
967 {
968 // for atomic, this will be done by rename in Close()
969 unlink(FileName.c_str());
970 }
971 if ((Mode & Empty) == Empty)
972 {
973 struct stat Buf;
974 if (lstat(FileName.c_str(),&Buf) == 0 && S_ISLNK(Buf.st_mode))
975 unlink(FileName.c_str());
976 }
977
978 int fileflags = 0;
979 #define if_FLAGGED_SET(FLAG, MODE) if ((Mode & FLAG) == FLAG) fileflags |= MODE
980 if_FLAGGED_SET(ReadWrite, O_RDWR);
981 else if_FLAGGED_SET(ReadOnly, O_RDONLY);
982 else if_FLAGGED_SET(WriteOnly, O_WRONLY);
983
984 if_FLAGGED_SET(Create, O_CREAT);
985 if_FLAGGED_SET(Empty, O_TRUNC);
986 if_FLAGGED_SET(Exclusive, O_EXCL);
987 #undef if_FLAGGED_SET
988
989 if ((Mode & Atomic) == Atomic)
990 {
991 char *name = strdup((FileName + ".XXXXXX").c_str());
992
993 if((iFd = mkstemp(name)) == -1)
994 {
995 free(name);
996 return FileFdErrno("mkstemp", "Could not create temporary file for %s", FileName.c_str());
997 }
998
999 TemporaryFileName = string(name);
1000 free(name);
1001
1002 if(Perms != 600 && fchmod(iFd, Perms) == -1)
1003 return FileFdErrno("fchmod", "Could not change permissions for temporary file %s", TemporaryFileName.c_str());
1004 }
1005 else
1006 iFd = open(FileName.c_str(), fileflags, Perms);
1007
1008 this->FileName = FileName;
1009 if (iFd == -1 || OpenInternDescriptor(Mode, compressor) == false)
1010 {
1011 if (iFd != -1)
1012 {
1013 close (iFd);
1014 iFd = -1;
1015 }
1016 return FileFdErrno("open",_("Could not open file %s"), FileName.c_str());
1017 }
1018
1019 SetCloseExec(iFd,true);
1020 return true;
1021 }
1022 /*}}}*/
1023 // FileFd::OpenDescriptor - Open a filedescriptor /*{{{*/
1024 // ---------------------------------------------------------------------
1025 /* */
1026 bool FileFd::OpenDescriptor(int Fd, unsigned int const Mode, CompressMode Compress, bool AutoClose)
1027 {
1028 std::vector<APT::Configuration::Compressor> const compressors = APT::Configuration::getCompressors();
1029 std::vector<APT::Configuration::Compressor>::const_iterator compressor = compressors.begin();
1030 std::string name;
1031
1032 // compat with the old API
1033 if (Mode == ReadOnlyGzip && Compress == None)
1034 Compress = Gzip;
1035
1036 switch (Compress)
1037 {
1038 case None: name = "."; break;
1039 case Gzip: name = "gzip"; break;
1040 case Bzip2: name = "bzip2"; break;
1041 case Lzma: name = "lzma"; break;
1042 case Xz: name = "xz"; break;
1043 case Auto:
1044 case Extension:
1045 if (AutoClose == true && Fd != -1)
1046 close(Fd);
1047 return FileFdError("Opening Fd %d in Auto or Extension compression mode is not supported", Fd);
1048 }
1049 for (; compressor != compressors.end(); ++compressor)
1050 if (compressor->Name == name)
1051 break;
1052 if (compressor == compressors.end())
1053 {
1054 if (AutoClose == true && Fd != -1)
1055 close(Fd);
1056 return FileFdError("Can't find a configured compressor %s for file %s", name.c_str(), FileName.c_str());
1057 }
1058 return OpenDescriptor(Fd, Mode, *compressor, AutoClose);
1059 }
1060 bool FileFd::OpenDescriptor(int Fd, unsigned int const Mode, APT::Configuration::Compressor const &compressor, bool AutoClose)
1061 {
1062 Close();
1063 Flags = (AutoClose) ? FileFd::AutoClose : 0;
1064 if (AutoClose == false && (
1065 #ifdef HAVE_ZLIB
1066 compressor.Name == "gzip" ||
1067 #endif
1068 #ifdef HAVE_BZ2
1069 compressor.Name == "bzip2" ||
1070 #endif
1071 false))
1072 {
1073 // Need to duplicate fd here or gzclose for cleanup will close the fd as well
1074 iFd = dup(Fd);
1075 }
1076 else
1077 iFd = Fd;
1078 this->FileName = "";
1079 if (Fd == -1 || OpenInternDescriptor(Mode, compressor) == false)
1080 {
1081 if (iFd != -1 && (
1082 #ifdef HAVE_ZLIB
1083 compressor.Name == "gzip" ||
1084 #endif
1085 #ifdef HAVE_BZ2
1086 compressor.Name == "bzip2" ||
1087 #endif
1088 AutoClose == true))
1089 {
1090 close (iFd);
1091 iFd = -1;
1092 }
1093 return FileFdError(_("Could not open file descriptor %d"), Fd);
1094 }
1095 return true;
1096 }
1097 bool FileFd::OpenInternDescriptor(unsigned int const Mode, APT::Configuration::Compressor const &compressor)
1098 {
1099 if (compressor.Name == "." || compressor.Binary.empty() == true)
1100 return true;
1101
1102 if (d == NULL)
1103 {
1104 d = new FileFdPrivate();
1105 d->openmode = Mode;
1106 d->compressor = compressor;
1107 }
1108
1109 #ifdef HAVE_ZLIB
1110 if (compressor.Name == "gzip")
1111 {
1112 if (d->gz != NULL)
1113 {
1114 gzclose(d->gz);
1115 d->gz = NULL;
1116 }
1117 if ((Mode & ReadWrite) == ReadWrite)
1118 d->gz = gzdopen(iFd, "r+");
1119 else if ((Mode & WriteOnly) == WriteOnly)
1120 d->gz = gzdopen(iFd, "w");
1121 else
1122 d->gz = gzdopen(iFd, "r");
1123 if (d->gz == NULL)
1124 return false;
1125 Flags |= Compressed;
1126 return true;
1127 }
1128 #endif
1129 #ifdef HAVE_BZ2
1130 if (compressor.Name == "bzip2")
1131 {
1132 if (d->bz2 != NULL)
1133 {
1134 BZ2_bzclose(d->bz2);
1135 d->bz2 = NULL;
1136 }
1137 if ((Mode & ReadWrite) == ReadWrite)
1138 d->bz2 = BZ2_bzdopen(iFd, "r+");
1139 else if ((Mode & WriteOnly) == WriteOnly)
1140 d->bz2 = BZ2_bzdopen(iFd, "w");
1141 else
1142 d->bz2 = BZ2_bzdopen(iFd, "r");
1143 if (d->bz2 == NULL)
1144 return false;
1145 Flags |= Compressed;
1146 return true;
1147 }
1148 #endif
1149
1150 // collect zombies here in case we reopen
1151 if (d->compressor_pid > 0)
1152 ExecWait(d->compressor_pid, "FileFdCompressor", true);
1153
1154 if ((Mode & ReadWrite) == ReadWrite)
1155 return FileFdError("ReadWrite mode is not supported for file %s", FileName.c_str());
1156
1157 bool const Comp = (Mode & WriteOnly) == WriteOnly;
1158 if (Comp == false)
1159 {
1160 // Handle 'decompression' of empty files
1161 struct stat Buf;
1162 fstat(iFd, &Buf);
1163 if (Buf.st_size == 0 && S_ISFIFO(Buf.st_mode) == false)
1164 return true;
1165
1166 // We don't need the file open - instead let the compressor open it
1167 // as he properly knows better how to efficiently read from 'his' file
1168 if (FileName.empty() == false)
1169 {
1170 close(iFd);
1171 iFd = -1;
1172 }
1173 }
1174
1175 // Create a data pipe
1176 int Pipe[2] = {-1,-1};
1177 if (pipe(Pipe) != 0)
1178 return FileFdErrno("pipe",_("Failed to create subprocess IPC"));
1179 for (int J = 0; J != 2; J++)
1180 SetCloseExec(Pipe[J],true);
1181
1182 d->compressed_fd = iFd;
1183 d->pipe = true;
1184
1185 if (Comp == true)
1186 iFd = Pipe[1];
1187 else
1188 iFd = Pipe[0];
1189
1190 // The child..
1191 d->compressor_pid = ExecFork();
1192 if (d->compressor_pid == 0)
1193 {
1194 if (Comp == true)
1195 {
1196 dup2(d->compressed_fd,STDOUT_FILENO);
1197 dup2(Pipe[0],STDIN_FILENO);
1198 }
1199 else
1200 {
1201 if (FileName.empty() == true)
1202 dup2(d->compressed_fd,STDIN_FILENO);
1203 dup2(Pipe[1],STDOUT_FILENO);
1204 }
1205 int const nullfd = open("/dev/null", O_WRONLY);
1206 if (nullfd != -1)
1207 {
1208 dup2(nullfd,STDERR_FILENO);
1209 close(nullfd);
1210 }
1211
1212 SetCloseExec(STDOUT_FILENO,false);
1213 SetCloseExec(STDIN_FILENO,false);
1214
1215 std::vector<char const*> Args;
1216 Args.push_back(compressor.Binary.c_str());
1217 std::vector<std::string> const * const addArgs =
1218 (Comp == true) ? &(compressor.CompressArgs) : &(compressor.UncompressArgs);
1219 for (std::vector<std::string>::const_iterator a = addArgs->begin();
1220 a != addArgs->end(); ++a)
1221 Args.push_back(a->c_str());
1222 if (Comp == false && FileName.empty() == false)
1223 {
1224 Args.push_back("--stdout");
1225 if (TemporaryFileName.empty() == false)
1226 Args.push_back(TemporaryFileName.c_str());
1227 else
1228 Args.push_back(FileName.c_str());
1229 }
1230 Args.push_back(NULL);
1231
1232 execvp(Args[0],(char **)&Args[0]);
1233 cerr << _("Failed to exec compressor ") << Args[0] << endl;
1234 _exit(100);
1235 }
1236 if (Comp == true)
1237 close(Pipe[0]);
1238 else
1239 close(Pipe[1]);
1240
1241 return true;
1242 }
1243 /*}}}*/
1244 // FileFd::~File - Closes the file /*{{{*/
1245 // ---------------------------------------------------------------------
1246 /* If the proper modes are selected then we close the Fd and possibly
1247 unlink the file on error. */
1248 FileFd::~FileFd()
1249 {
1250 Close();
1251 if (d != NULL)
1252 d->CloseDown(FileName);
1253 delete d;
1254 d = NULL;
1255 }
1256 /*}}}*/
1257 // FileFd::Read - Read a bit of the file /*{{{*/
1258 // ---------------------------------------------------------------------
1259 /* We are carefull to handle interruption by a signal while reading
1260 gracefully. */
1261 bool FileFd::Read(void *To,unsigned long long Size,unsigned long long *Actual)
1262 {
1263 ssize_t Res;
1264 errno = 0;
1265 if (Actual != 0)
1266 *Actual = 0;
1267 *((char *)To) = '\0';
1268 do
1269 {
1270 #ifdef HAVE_ZLIB
1271 if (d != NULL && d->gz != NULL)
1272 Res = gzread(d->gz,To,Size);
1273 else
1274 #endif
1275 #ifdef HAVE_BZ2
1276 if (d != NULL && d->bz2 != NULL)
1277 Res = BZ2_bzread(d->bz2,To,Size);
1278 else
1279 #endif
1280 Res = read(iFd,To,Size);
1281
1282 if (Res < 0)
1283 {
1284 if (errno == EINTR)
1285 continue;
1286 #ifdef HAVE_ZLIB
1287 if (d != NULL && d->gz != NULL)
1288 {
1289 int err;
1290 char const * const errmsg = gzerror(d->gz, &err);
1291 if (err != Z_ERRNO)
1292 return FileFdError("gzread: %s (%d: %s)", _("Read error"), err, errmsg);
1293 }
1294 #endif
1295 #ifdef HAVE_BZ2
1296 if (d != NULL && d->bz2 != NULL)
1297 {
1298 int err;
1299 char const * const errmsg = BZ2_bzerror(d->bz2, &err);
1300 if (err != BZ_IO_ERROR)
1301 return FileFdError("BZ2_bzread: %s (%d: %s)", _("Read error"), err, errmsg);
1302 }
1303 #endif
1304 return FileFdErrno("read",_("Read error"));
1305 }
1306
1307 To = (char *)To + Res;
1308 Size -= Res;
1309 if (d != NULL)
1310 d->seekpos += Res;
1311 if (Actual != 0)
1312 *Actual += Res;
1313 }
1314 while (Res > 0 && Size > 0);
1315
1316 if (Size == 0)
1317 return true;
1318
1319 // Eof handling
1320 if (Actual != 0)
1321 {
1322 Flags |= HitEof;
1323 return true;
1324 }
1325
1326 return FileFdError(_("read, still have %llu to read but none left"), Size);
1327 }
1328 /*}}}*/
1329 // FileFd::ReadLine - Read a complete line from the file /*{{{*/
1330 // ---------------------------------------------------------------------
1331 /* Beware: This method can be quiet slow for big buffers on UNcompressed
1332 files because of the naive implementation! */
1333 char* FileFd::ReadLine(char *To, unsigned long long const Size)
1334 {
1335 *To = '\0';
1336 #ifdef HAVE_ZLIB
1337 if (d != NULL && d->gz != NULL)
1338 return gzgets(d->gz, To, Size);
1339 #endif
1340
1341 unsigned long long read = 0;
1342 while ((Size - 1) != read)
1343 {
1344 unsigned long long done = 0;
1345 if (Read(To + read, 1, &done) == false)
1346 return NULL;
1347 if (done == 0)
1348 break;
1349 if (To[read++] == '\n')
1350 break;
1351 }
1352 if (read == 0)
1353 return NULL;
1354 To[read] = '\0';
1355 return To;
1356 }
1357 /*}}}*/
1358 // FileFd::Write - Write to the file /*{{{*/
1359 // ---------------------------------------------------------------------
1360 /* */
1361 bool FileFd::Write(const void *From,unsigned long long Size)
1362 {
1363 ssize_t Res;
1364 errno = 0;
1365 do
1366 {
1367 #ifdef HAVE_ZLIB
1368 if (d != NULL && d->gz != NULL)
1369 Res = gzwrite(d->gz,From,Size);
1370 else
1371 #endif
1372 #ifdef HAVE_BZ2
1373 if (d != NULL && d->bz2 != NULL)
1374 Res = BZ2_bzwrite(d->bz2,(void*)From,Size);
1375 else
1376 #endif
1377 Res = write(iFd,From,Size);
1378 if (Res < 0 && errno == EINTR)
1379 continue;
1380 if (Res < 0)
1381 {
1382 #ifdef HAVE_ZLIB
1383 if (d != NULL && d->gz != NULL)
1384 {
1385 int err;
1386 char const * const errmsg = gzerror(d->gz, &err);
1387 if (err != Z_ERRNO)
1388 return FileFdError("gzwrite: %s (%d: %s)", _("Write error"), err, errmsg);
1389 }
1390 #endif
1391 #ifdef HAVE_BZ2
1392 if (d != NULL && d->bz2 != NULL)
1393 {
1394 int err;
1395 char const * const errmsg = BZ2_bzerror(d->bz2, &err);
1396 if (err != BZ_IO_ERROR)
1397 return FileFdError("BZ2_bzwrite: %s (%d: %s)", _("Write error"), err, errmsg);
1398 }
1399 #endif
1400 return FileFdErrno("write",_("Write error"));
1401 }
1402
1403 From = (char *)From + Res;
1404 Size -= Res;
1405 if (d != NULL)
1406 d->seekpos += Res;
1407 }
1408 while (Res > 0 && Size > 0);
1409
1410 if (Size == 0)
1411 return true;
1412
1413 return FileFdError(_("write, still have %llu to write but couldn't"), Size);
1414 }
1415 bool FileFd::Write(int Fd, const void *From, unsigned long long Size)
1416 {
1417 ssize_t Res;
1418 errno = 0;
1419 do
1420 {
1421 Res = write(Fd,From,Size);
1422 if (Res < 0 && errno == EINTR)
1423 continue;
1424 if (Res < 0)
1425 return _error->Errno("write",_("Write error"));
1426
1427 From = (char *)From + Res;
1428 Size -= Res;
1429 }
1430 while (Res > 0 && Size > 0);
1431
1432 if (Size == 0)
1433 return true;
1434
1435 return _error->Error(_("write, still have %llu to write but couldn't"), Size);
1436 }
1437 /*}}}*/
1438 // FileFd::Seek - Seek in the file /*{{{*/
1439 // ---------------------------------------------------------------------
1440 /* */
1441 bool FileFd::Seek(unsigned long long To)
1442 {
1443 if (d != NULL && (d->pipe == true
1444 #ifdef HAVE_BZ2
1445 || d->bz2 != NULL
1446 #endif
1447 ))
1448 {
1449 // Our poor man seeking in pipes is costly, so try to avoid it
1450 unsigned long long seekpos = Tell();
1451 if (seekpos == To)
1452 return true;
1453 else if (seekpos < To)
1454 return Skip(To - seekpos);
1455
1456 if ((d->openmode & ReadOnly) != ReadOnly)
1457 return FileFdError("Reopen is only implemented for read-only files!");
1458 #ifdef HAVE_BZ2
1459 if (d->bz2 != NULL)
1460 {
1461 BZ2_bzclose(d->bz2);
1462 d->bz2 = NULL;
1463 }
1464 #endif
1465 if (iFd != -1)
1466 close(iFd);
1467 iFd = -1;
1468 if (TemporaryFileName.empty() == false)
1469 iFd = open(TemporaryFileName.c_str(), O_RDONLY);
1470 else if (FileName.empty() == false)
1471 iFd = open(FileName.c_str(), O_RDONLY);
1472 else
1473 {
1474 if (d->compressed_fd > 0)
1475 if (lseek(d->compressed_fd, 0, SEEK_SET) != 0)
1476 iFd = d->compressed_fd;
1477 if (iFd < 0)
1478 return FileFdError("Reopen is not implemented for pipes opened with FileFd::OpenDescriptor()!");
1479 }
1480
1481 if (OpenInternDescriptor(d->openmode, d->compressor) == false)
1482 return FileFdError("Seek on file %s because it couldn't be reopened", FileName.c_str());
1483
1484 if (To != 0)
1485 return Skip(To);
1486
1487 d->seekpos = To;
1488 return true;
1489 }
1490 off_t res;
1491 #ifdef HAVE_ZLIB
1492 if (d != NULL && d->gz)
1493 res = gzseek(d->gz,To,SEEK_SET);
1494 else
1495 #endif
1496 res = lseek(iFd,To,SEEK_SET);
1497 if (res != (off_t)To)
1498 return FileFdError("Unable to seek to %llu", To);
1499
1500 if (d != NULL)
1501 d->seekpos = To;
1502 return true;
1503 }
1504 /*}}}*/
1505 // FileFd::Skip - Seek in the file /*{{{*/
1506 // ---------------------------------------------------------------------
1507 /* */
1508 bool FileFd::Skip(unsigned long long Over)
1509 {
1510 if (d != NULL && (d->pipe == true
1511 #ifdef HAVE_BZ2
1512 || d->bz2 != NULL
1513 #endif
1514 ))
1515 {
1516 d->seekpos += Over;
1517 char buffer[1024];
1518 while (Over != 0)
1519 {
1520 unsigned long long toread = std::min((unsigned long long) sizeof(buffer), Over);
1521 if (Read(buffer, toread) == false)
1522 return FileFdError("Unable to seek ahead %llu",Over);
1523 Over -= toread;
1524 }
1525 return true;
1526 }
1527
1528 off_t res;
1529 #ifdef HAVE_ZLIB
1530 if (d != NULL && d->gz != NULL)
1531 res = gzseek(d->gz,Over,SEEK_CUR);
1532 else
1533 #endif
1534 res = lseek(iFd,Over,SEEK_CUR);
1535 if (res < 0)
1536 return FileFdError("Unable to seek ahead %llu",Over);
1537 if (d != NULL)
1538 d->seekpos = res;
1539
1540 return true;
1541 }
1542 /*}}}*/
1543 // FileFd::Truncate - Truncate the file /*{{{*/
1544 // ---------------------------------------------------------------------
1545 /* */
1546 bool FileFd::Truncate(unsigned long long To)
1547 {
1548 // truncating /dev/null is always successful - as we get an error otherwise
1549 if (To == 0 && FileName == "/dev/null")
1550 return true;
1551 #if defined HAVE_ZLIB || defined HAVE_BZ2
1552 if (d != NULL && (d->gz != NULL || d->bz2 != NULL))
1553 return FileFdError("Truncating compressed files is not implemented (%s)", FileName.c_str());
1554 #endif
1555 if (ftruncate(iFd,To) != 0)
1556 return FileFdError("Unable to truncate to %llu",To);
1557
1558 return true;
1559 }
1560 /*}}}*/
1561 // FileFd::Tell - Current seek position /*{{{*/
1562 // ---------------------------------------------------------------------
1563 /* */
1564 unsigned long long FileFd::Tell()
1565 {
1566 // In theory, we could just return seekpos here always instead of
1567 // seeking around, but not all users of FileFd use always Seek() and co
1568 // so d->seekpos isn't always true and we can just use it as a hint if
1569 // we have nothing else, but not always as an authority…
1570 if (d != NULL && (d->pipe == true
1571 #ifdef HAVE_BZ2
1572 || d->bz2 != NULL
1573 #endif
1574 ))
1575 return d->seekpos;
1576
1577 off_t Res;
1578 #ifdef HAVE_ZLIB
1579 if (d != NULL && d->gz != NULL)
1580 Res = gztell(d->gz);
1581 else
1582 #endif
1583 Res = lseek(iFd,0,SEEK_CUR);
1584 if (Res == (off_t)-1)
1585 FileFdErrno("lseek","Failed to determine the current file position");
1586 if (d != NULL)
1587 d->seekpos = Res;
1588 return Res;
1589 }
1590 /*}}}*/
1591 // FileFd::FileSize - Return the size of the file /*{{{*/
1592 // ---------------------------------------------------------------------
1593 /* */
1594 unsigned long long FileFd::FileSize()
1595 {
1596 struct stat Buf;
1597 if ((d == NULL || d->pipe == false) && fstat(iFd,&Buf) != 0)
1598 return FileFdErrno("fstat","Unable to determine the file size");
1599
1600 // for compressor pipes st_size is undefined and at 'best' zero
1601 if ((d != NULL && d->pipe == true) || S_ISFIFO(Buf.st_mode))
1602 {
1603 // we set it here, too, as we get the info here for free
1604 // in theory the Open-methods should take care of it already
1605 if (d != NULL)
1606 d->pipe = true;
1607 if (stat(FileName.c_str(), &Buf) != 0)
1608 return FileFdErrno("stat","Unable to determine the file size");
1609 }
1610
1611 return Buf.st_size;
1612 }
1613 /*}}}*/
1614 // FileFd::Size - Return the size of the content in the file /*{{{*/
1615 // ---------------------------------------------------------------------
1616 /* */
1617 unsigned long long FileFd::Size()
1618 {
1619 unsigned long long size = FileSize();
1620
1621 // for compressor pipes st_size is undefined and at 'best' zero,
1622 // so we 'read' the content and 'seek' back - see there
1623 if (d != NULL && (d->pipe == true
1624 #ifdef HAVE_BZ2
1625 || (d->bz2 && size > 0)
1626 #endif
1627 ))
1628 {
1629 unsigned long long const oldSeek = Tell();
1630 char ignore[1000];
1631 unsigned long long read = 0;
1632 do {
1633 if (Read(ignore, sizeof(ignore), &read) == false)
1634 {
1635 Seek(oldSeek);
1636 return 0;
1637 }
1638 } while(read != 0);
1639 size = Tell();
1640 Seek(oldSeek);
1641 }
1642 #ifdef HAVE_ZLIB
1643 // only check gzsize if we are actually a gzip file, just checking for
1644 // "gz" is not sufficient as uncompressed files could be opened with
1645 // gzopen in "direct" mode as well
1646 else if (d != NULL && d->gz && !gzdirect(d->gz) && size > 0)
1647 {
1648 off_t const oldPos = lseek(iFd,0,SEEK_CUR);
1649 /* unfortunately zlib.h doesn't provide a gzsize(), so we have to do
1650 * this ourselves; the original (uncompressed) file size is the last 32
1651 * bits of the file */
1652 // FIXME: Size for gz-files is limited by 32bit… no largefile support
1653 if (lseek(iFd, -4, SEEK_END) < 0)
1654 {
1655 FileFdErrno("lseek","Unable to seek to end of gzipped file");
1656 return 0;
1657 }
1658 size = 0;
1659 if (read(iFd, &size, 4) != 4)
1660 {
1661 FileFdErrno("read","Unable to read original size of gzipped file");
1662 return 0;
1663 }
1664
1665 #ifdef WORDS_BIGENDIAN
1666 uint32_t tmp_size = size;
1667 uint8_t const * const p = (uint8_t const * const) &tmp_size;
1668 tmp_size = (p[3] << 24) | (p[2] << 16) | (p[1] << 8) | p[0];
1669 size = tmp_size;
1670 #endif
1671
1672 if (lseek(iFd, oldPos, SEEK_SET) < 0)
1673 {
1674 FileFdErrno("lseek","Unable to seek in gzipped file");
1675 return 0;
1676 }
1677
1678 return size;
1679 }
1680 #endif
1681
1682 return size;
1683 }
1684 /*}}}*/
1685 // FileFd::ModificationTime - Return the time of last touch /*{{{*/
1686 // ---------------------------------------------------------------------
1687 /* */
1688 time_t FileFd::ModificationTime()
1689 {
1690 struct stat Buf;
1691 if ((d == NULL || d->pipe == false) && fstat(iFd,&Buf) != 0)
1692 {
1693 FileFdErrno("fstat","Unable to determine the modification time of file %s", FileName.c_str());
1694 return 0;
1695 }
1696
1697 // for compressor pipes st_size is undefined and at 'best' zero
1698 if ((d != NULL && d->pipe == true) || S_ISFIFO(Buf.st_mode))
1699 {
1700 // we set it here, too, as we get the info here for free
1701 // in theory the Open-methods should take care of it already
1702 if (d != NULL)
1703 d->pipe = true;
1704 if (stat(FileName.c_str(), &Buf) != 0)
1705 {
1706 FileFdErrno("fstat","Unable to determine the modification time of file %s", FileName.c_str());
1707 return 0;
1708 }
1709 }
1710
1711 return Buf.st_mtime;
1712 }
1713 /*}}}*/
1714 // FileFd::Close - Close the file if the close flag is set /*{{{*/
1715 // ---------------------------------------------------------------------
1716 /* */
1717 bool FileFd::Close()
1718 {
1719 if (iFd == -1)
1720 return true;
1721
1722 bool Res = true;
1723 if ((Flags & AutoClose) == AutoClose)
1724 {
1725 if ((Flags & Compressed) != Compressed && iFd > 0 && close(iFd) != 0)
1726 Res &= _error->Errno("close",_("Problem closing the file %s"), FileName.c_str());
1727
1728 if (d != NULL)
1729 {
1730 Res &= d->CloseDown(FileName);
1731 delete d;
1732 d = NULL;
1733 }
1734 }
1735
1736 if ((Flags & Replace) == Replace) {
1737 if (rename(TemporaryFileName.c_str(), FileName.c_str()) != 0)
1738 Res &= _error->Errno("rename",_("Problem renaming the file %s to %s"), TemporaryFileName.c_str(), FileName.c_str());
1739
1740 FileName = TemporaryFileName; // for the unlink() below.
1741 TemporaryFileName.clear();
1742 }
1743
1744 iFd = -1;
1745
1746 if ((Flags & Fail) == Fail && (Flags & DelOnFail) == DelOnFail &&
1747 FileName.empty() == false)
1748 if (unlink(FileName.c_str()) != 0)
1749 Res &= _error->WarningE("unlnk",_("Problem unlinking the file %s"), FileName.c_str());
1750
1751 if (Res == false)
1752 Flags |= Fail;
1753 return Res;
1754 }
1755 /*}}}*/
1756 // FileFd::Sync - Sync the file /*{{{*/
1757 // ---------------------------------------------------------------------
1758 /* */
1759 bool FileFd::Sync()
1760 {
1761 if (fsync(iFd) != 0)
1762 return FileFdErrno("sync",_("Problem syncing the file"));
1763 return true;
1764 }
1765 /*}}}*/
1766 // FileFd::FileFdErrno - set Fail and call _error->Errno *{{{*/
1767 bool FileFd::FileFdErrno(const char *Function, const char *Description,...)
1768 {
1769 Flags |= Fail;
1770 va_list args;
1771 size_t msgSize = 400;
1772 int const errsv = errno;
1773 while (true)
1774 {
1775 va_start(args,Description);
1776 if (_error->InsertErrno(GlobalError::ERROR, Function, Description, args, errsv, msgSize) == false)
1777 break;
1778 va_end(args);
1779 }
1780 return false;
1781 }
1782 /*}}}*/
1783 // FileFd::FileFdError - set Fail and call _error->Error *{{{*/
1784 bool FileFd::FileFdError(const char *Description,...) {
1785 Flags |= Fail;
1786 va_list args;
1787 size_t msgSize = 400;
1788 while (true)
1789 {
1790 va_start(args,Description);
1791 if (_error->Insert(GlobalError::ERROR, Description, args, msgSize) == false)
1792 break;
1793 va_end(args);
1794 }
1795 return false;
1796 }
1797 /*}}}*/
1798
1799 gzFile FileFd::gzFd() { return (gzFile) d->gz; }
1800
1801
1802 // Glob - wrapper around "glob()" /*{{{*/
1803 // ---------------------------------------------------------------------
1804 /* */
1805 std::vector<std::string> Glob(std::string const &pattern, int flags)
1806 {
1807 std::vector<std::string> result;
1808 glob_t globbuf;
1809 int glob_res;
1810 unsigned int i;
1811
1812 glob_res = glob(pattern.c_str(), flags, NULL, &globbuf);
1813
1814 if (glob_res != 0)
1815 {
1816 if(glob_res != GLOB_NOMATCH) {
1817 _error->Errno("glob", "Problem with glob");
1818 return result;
1819 }
1820 }
1821
1822 // append results
1823 for(i=0;i<globbuf.gl_pathc;i++)
1824 result.push_back(string(globbuf.gl_pathv[i]));
1825
1826 globfree(&globbuf);
1827 return result;
1828 }
1829 /*}}}*/