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