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