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