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