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