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