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