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