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