]> git.saurik.com Git - apt.git/blob - apt-pkg/contrib/fileutl.cc
move fd duplication closer to the gz/bz2 open calls
[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 iFd = Fd;
1071 this->FileName = "";
1072 if (OpenInternDescriptor(Mode, compressor) == false)
1073 {
1074 if (iFd != -1 && (
1075 (Flags & Compressed) == Compressed ||
1076 AutoClose == true))
1077 {
1078 close (iFd);
1079 iFd = -1;
1080 }
1081 return FileFdError(_("Could not open file descriptor %d"), Fd);
1082 }
1083 return true;
1084 }
1085 bool FileFd::OpenInternDescriptor(unsigned int const Mode, APT::Configuration::Compressor const &compressor)
1086 {
1087 if (iFd == -1)
1088 return false;
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 if (AutoClose == false && (
1098 #ifdef HAVE_ZLIB
1099 compressor.Name == "gzip" ||
1100 #endif
1101 #ifdef HAVE_BZ2
1102 compressor.Name == "bzip2" ||
1103 #endif
1104 false))
1105 {
1106 // Need to duplicate fd here or gz/bz2 close for cleanup will close the fd as well
1107 int const internFd = dup(iFd);
1108 if (internFd == -1)
1109 return FileFdErrno("OpenInternDescriptor", _("Could not open file descriptor %d"), iFd);
1110 iFd = internFd;
1111 }
1112 }
1113
1114 #ifdef HAVE_ZLIB
1115 if (compressor.Name == "gzip")
1116 {
1117 if (d->gz != NULL)
1118 {
1119 gzclose(d->gz);
1120 d->gz = NULL;
1121 }
1122 if ((Mode & ReadWrite) == ReadWrite)
1123 d->gz = gzdopen(iFd, "r+");
1124 else if ((Mode & WriteOnly) == WriteOnly)
1125 d->gz = gzdopen(iFd, "w");
1126 else
1127 d->gz = gzdopen(iFd, "r");
1128 if (d->gz == NULL)
1129 return false;
1130 Flags |= Compressed;
1131 return true;
1132 }
1133 #endif
1134 #ifdef HAVE_BZ2
1135 if (compressor.Name == "bzip2")
1136 {
1137 if (d->bz2 != NULL)
1138 {
1139 BZ2_bzclose(d->bz2);
1140 d->bz2 = NULL;
1141 }
1142 if ((Mode & ReadWrite) == ReadWrite)
1143 d->bz2 = BZ2_bzdopen(iFd, "r+");
1144 else if ((Mode & WriteOnly) == WriteOnly)
1145 d->bz2 = BZ2_bzdopen(iFd, "w");
1146 else
1147 d->bz2 = BZ2_bzdopen(iFd, "r");
1148 if (d->bz2 == NULL)
1149 return false;
1150 Flags |= Compressed;
1151 return true;
1152 }
1153 #endif
1154
1155 // collect zombies here in case we reopen
1156 if (d->compressor_pid > 0)
1157 ExecWait(d->compressor_pid, "FileFdCompressor", true);
1158
1159 if ((Mode & ReadWrite) == ReadWrite)
1160 return FileFdError("ReadWrite mode is not supported for file %s", FileName.c_str());
1161
1162 bool const Comp = (Mode & WriteOnly) == WriteOnly;
1163 if (Comp == false)
1164 {
1165 // Handle 'decompression' of empty files
1166 struct stat Buf;
1167 fstat(iFd, &Buf);
1168 if (Buf.st_size == 0 && S_ISFIFO(Buf.st_mode) == false)
1169 return true;
1170
1171 // We don't need the file open - instead let the compressor open it
1172 // as he properly knows better how to efficiently read from 'his' file
1173 if (FileName.empty() == false)
1174 {
1175 close(iFd);
1176 iFd = -1;
1177 }
1178 }
1179
1180 // Create a data pipe
1181 int Pipe[2] = {-1,-1};
1182 if (pipe(Pipe) != 0)
1183 return FileFdErrno("pipe",_("Failed to create subprocess IPC"));
1184 for (int J = 0; J != 2; J++)
1185 SetCloseExec(Pipe[J],true);
1186
1187 d->compressed_fd = iFd;
1188 d->pipe = true;
1189
1190 if (Comp == true)
1191 iFd = Pipe[1];
1192 else
1193 iFd = Pipe[0];
1194
1195 // The child..
1196 d->compressor_pid = ExecFork();
1197 if (d->compressor_pid == 0)
1198 {
1199 if (Comp == true)
1200 {
1201 dup2(d->compressed_fd,STDOUT_FILENO);
1202 dup2(Pipe[0],STDIN_FILENO);
1203 }
1204 else
1205 {
1206 if (FileName.empty() == true)
1207 dup2(d->compressed_fd,STDIN_FILENO);
1208 dup2(Pipe[1],STDOUT_FILENO);
1209 }
1210 int const nullfd = open("/dev/null", O_WRONLY);
1211 if (nullfd != -1)
1212 {
1213 dup2(nullfd,STDERR_FILENO);
1214 close(nullfd);
1215 }
1216
1217 SetCloseExec(STDOUT_FILENO,false);
1218 SetCloseExec(STDIN_FILENO,false);
1219
1220 std::vector<char const*> Args;
1221 Args.push_back(compressor.Binary.c_str());
1222 std::vector<std::string> const * const addArgs =
1223 (Comp == true) ? &(compressor.CompressArgs) : &(compressor.UncompressArgs);
1224 for (std::vector<std::string>::const_iterator a = addArgs->begin();
1225 a != addArgs->end(); ++a)
1226 Args.push_back(a->c_str());
1227 if (Comp == false && FileName.empty() == false)
1228 {
1229 Args.push_back("--stdout");
1230 if (TemporaryFileName.empty() == false)
1231 Args.push_back(TemporaryFileName.c_str());
1232 else
1233 Args.push_back(FileName.c_str());
1234 }
1235 Args.push_back(NULL);
1236
1237 execvp(Args[0],(char **)&Args[0]);
1238 cerr << _("Failed to exec compressor ") << Args[0] << endl;
1239 _exit(100);
1240 }
1241 if (Comp == true)
1242 close(Pipe[0]);
1243 else
1244 close(Pipe[1]);
1245
1246 return true;
1247 }
1248 /*}}}*/
1249 // FileFd::~File - Closes the file /*{{{*/
1250 // ---------------------------------------------------------------------
1251 /* If the proper modes are selected then we close the Fd and possibly
1252 unlink the file on error. */
1253 FileFd::~FileFd()
1254 {
1255 Close();
1256 if (d != NULL)
1257 d->CloseDown(FileName);
1258 delete d;
1259 d = NULL;
1260 }
1261 /*}}}*/
1262 // FileFd::Read - Read a bit of the file /*{{{*/
1263 // ---------------------------------------------------------------------
1264 /* We are careful to handle interruption by a signal while reading
1265 gracefully. */
1266 bool FileFd::Read(void *To,unsigned long long Size,unsigned long long *Actual)
1267 {
1268 ssize_t Res;
1269 errno = 0;
1270 if (Actual != 0)
1271 *Actual = 0;
1272 *((char *)To) = '\0';
1273 do
1274 {
1275 #ifdef HAVE_ZLIB
1276 if (d != NULL && d->gz != NULL)
1277 Res = gzread(d->gz,To,Size);
1278 else
1279 #endif
1280 #ifdef HAVE_BZ2
1281 if (d != NULL && d->bz2 != NULL)
1282 Res = BZ2_bzread(d->bz2,To,Size);
1283 else
1284 #endif
1285 Res = read(iFd,To,Size);
1286
1287 if (Res < 0)
1288 {
1289 if (errno == EINTR)
1290 continue;
1291 #ifdef HAVE_ZLIB
1292 if (d != NULL && d->gz != NULL)
1293 {
1294 int err;
1295 char const * const errmsg = gzerror(d->gz, &err);
1296 if (err != Z_ERRNO)
1297 return FileFdError("gzread: %s (%d: %s)", _("Read error"), err, errmsg);
1298 }
1299 #endif
1300 #ifdef HAVE_BZ2
1301 if (d != NULL && d->bz2 != NULL)
1302 {
1303 int err;
1304 char const * const errmsg = BZ2_bzerror(d->bz2, &err);
1305 if (err != BZ_IO_ERROR)
1306 return FileFdError("BZ2_bzread: %s (%d: %s)", _("Read error"), err, errmsg);
1307 }
1308 #endif
1309 return FileFdErrno("read",_("Read error"));
1310 }
1311
1312 To = (char *)To + Res;
1313 Size -= Res;
1314 if (d != NULL)
1315 d->seekpos += Res;
1316 if (Actual != 0)
1317 *Actual += Res;
1318 }
1319 while (Res > 0 && Size > 0);
1320
1321 if (Size == 0)
1322 return true;
1323
1324 // Eof handling
1325 if (Actual != 0)
1326 {
1327 Flags |= HitEof;
1328 return true;
1329 }
1330
1331 return FileFdError(_("read, still have %llu to read but none left"), Size);
1332 }
1333 /*}}}*/
1334 // FileFd::ReadLine - Read a complete line from the file /*{{{*/
1335 // ---------------------------------------------------------------------
1336 /* Beware: This method can be quiet slow for big buffers on UNcompressed
1337 files because of the naive implementation! */
1338 char* FileFd::ReadLine(char *To, unsigned long long const Size)
1339 {
1340 *To = '\0';
1341 #ifdef HAVE_ZLIB
1342 if (d != NULL && d->gz != NULL)
1343 return gzgets(d->gz, To, Size);
1344 #endif
1345
1346 unsigned long long read = 0;
1347 while ((Size - 1) != read)
1348 {
1349 unsigned long long done = 0;
1350 if (Read(To + read, 1, &done) == false)
1351 return NULL;
1352 if (done == 0)
1353 break;
1354 if (To[read++] == '\n')
1355 break;
1356 }
1357 if (read == 0)
1358 return NULL;
1359 To[read] = '\0';
1360 return To;
1361 }
1362 /*}}}*/
1363 // FileFd::Write - Write to the file /*{{{*/
1364 // ---------------------------------------------------------------------
1365 /* */
1366 bool FileFd::Write(const void *From,unsigned long long Size)
1367 {
1368 ssize_t Res;
1369 errno = 0;
1370 do
1371 {
1372 #ifdef HAVE_ZLIB
1373 if (d != NULL && d->gz != NULL)
1374 Res = gzwrite(d->gz,From,Size);
1375 else
1376 #endif
1377 #ifdef HAVE_BZ2
1378 if (d != NULL && d->bz2 != NULL)
1379 Res = BZ2_bzwrite(d->bz2,(void*)From,Size);
1380 else
1381 #endif
1382 Res = write(iFd,From,Size);
1383 if (Res < 0 && errno == EINTR)
1384 continue;
1385 if (Res < 0)
1386 {
1387 #ifdef HAVE_ZLIB
1388 if (d != NULL && d->gz != NULL)
1389 {
1390 int err;
1391 char const * const errmsg = gzerror(d->gz, &err);
1392 if (err != Z_ERRNO)
1393 return FileFdError("gzwrite: %s (%d: %s)", _("Write error"), err, errmsg);
1394 }
1395 #endif
1396 #ifdef HAVE_BZ2
1397 if (d != NULL && d->bz2 != NULL)
1398 {
1399 int err;
1400 char const * const errmsg = BZ2_bzerror(d->bz2, &err);
1401 if (err != BZ_IO_ERROR)
1402 return FileFdError("BZ2_bzwrite: %s (%d: %s)", _("Write error"), err, errmsg);
1403 }
1404 #endif
1405 return FileFdErrno("write",_("Write error"));
1406 }
1407
1408 From = (char const *)From + Res;
1409 Size -= Res;
1410 if (d != NULL)
1411 d->seekpos += Res;
1412 }
1413 while (Res > 0 && Size > 0);
1414
1415 if (Size == 0)
1416 return true;
1417
1418 return FileFdError(_("write, still have %llu to write but couldn't"), Size);
1419 }
1420 bool FileFd::Write(int Fd, const void *From, unsigned long long Size)
1421 {
1422 ssize_t Res;
1423 errno = 0;
1424 do
1425 {
1426 Res = write(Fd,From,Size);
1427 if (Res < 0 && errno == EINTR)
1428 continue;
1429 if (Res < 0)
1430 return _error->Errno("write",_("Write error"));
1431
1432 From = (char const *)From + Res;
1433 Size -= Res;
1434 }
1435 while (Res > 0 && Size > 0);
1436
1437 if (Size == 0)
1438 return true;
1439
1440 return _error->Error(_("write, still have %llu to write but couldn't"), Size);
1441 }
1442 /*}}}*/
1443 // FileFd::Seek - Seek in the file /*{{{*/
1444 // ---------------------------------------------------------------------
1445 /* */
1446 bool FileFd::Seek(unsigned long long To)
1447 {
1448 if (d != NULL && (d->pipe == true
1449 #ifdef HAVE_BZ2
1450 || d->bz2 != NULL
1451 #endif
1452 ))
1453 {
1454 // Our poor man seeking in pipes is costly, so try to avoid it
1455 unsigned long long seekpos = Tell();
1456 if (seekpos == To)
1457 return true;
1458 else if (seekpos < To)
1459 return Skip(To - seekpos);
1460
1461 if ((d->openmode & ReadOnly) != ReadOnly)
1462 return FileFdError("Reopen is only implemented for read-only files!");
1463 #ifdef HAVE_BZ2
1464 if (d->bz2 != NULL)
1465 {
1466 BZ2_bzclose(d->bz2);
1467 d->bz2 = NULL;
1468 }
1469 #endif
1470 if (iFd != -1)
1471 close(iFd);
1472 iFd = -1;
1473 if (TemporaryFileName.empty() == false)
1474 iFd = open(TemporaryFileName.c_str(), O_RDONLY);
1475 else if (FileName.empty() == false)
1476 iFd = open(FileName.c_str(), O_RDONLY);
1477 else
1478 {
1479 if (d->compressed_fd > 0)
1480 if (lseek(d->compressed_fd, 0, SEEK_SET) != 0)
1481 iFd = d->compressed_fd;
1482 if (iFd < 0)
1483 return FileFdError("Reopen is not implemented for pipes opened with FileFd::OpenDescriptor()!");
1484 }
1485
1486 if (OpenInternDescriptor(d->openmode, d->compressor) == false)
1487 return FileFdError("Seek on file %s because it couldn't be reopened", FileName.c_str());
1488
1489 if (To != 0)
1490 return Skip(To);
1491
1492 d->seekpos = To;
1493 return true;
1494 }
1495 off_t res;
1496 #ifdef HAVE_ZLIB
1497 if (d != NULL && d->gz)
1498 res = gzseek(d->gz,To,SEEK_SET);
1499 else
1500 #endif
1501 res = lseek(iFd,To,SEEK_SET);
1502 if (res != (off_t)To)
1503 return FileFdError("Unable to seek to %llu", To);
1504
1505 if (d != NULL)
1506 d->seekpos = To;
1507 return true;
1508 }
1509 /*}}}*/
1510 // FileFd::Skip - Seek in the file /*{{{*/
1511 // ---------------------------------------------------------------------
1512 /* */
1513 bool FileFd::Skip(unsigned long long Over)
1514 {
1515 if (d != NULL && (d->pipe == true
1516 #ifdef HAVE_BZ2
1517 || d->bz2 != NULL
1518 #endif
1519 ))
1520 {
1521 d->seekpos += Over;
1522 char buffer[1024];
1523 while (Over != 0)
1524 {
1525 unsigned long long toread = std::min((unsigned long long) sizeof(buffer), Over);
1526 if (Read(buffer, toread) == false)
1527 return FileFdError("Unable to seek ahead %llu",Over);
1528 Over -= toread;
1529 }
1530 return true;
1531 }
1532
1533 off_t res;
1534 #ifdef HAVE_ZLIB
1535 if (d != NULL && d->gz != NULL)
1536 res = gzseek(d->gz,Over,SEEK_CUR);
1537 else
1538 #endif
1539 res = lseek(iFd,Over,SEEK_CUR);
1540 if (res < 0)
1541 return FileFdError("Unable to seek ahead %llu",Over);
1542 if (d != NULL)
1543 d->seekpos = res;
1544
1545 return true;
1546 }
1547 /*}}}*/
1548 // FileFd::Truncate - Truncate the file /*{{{*/
1549 // ---------------------------------------------------------------------
1550 /* */
1551 bool FileFd::Truncate(unsigned long long To)
1552 {
1553 // truncating /dev/null is always successful - as we get an error otherwise
1554 if (To == 0 && FileName == "/dev/null")
1555 return true;
1556 #if defined HAVE_ZLIB || defined HAVE_BZ2
1557 if (d != NULL && (d->gz != NULL || d->bz2 != NULL))
1558 return FileFdError("Truncating compressed files is not implemented (%s)", FileName.c_str());
1559 #endif
1560 if (ftruncate(iFd,To) != 0)
1561 return FileFdError("Unable to truncate to %llu",To);
1562
1563 return true;
1564 }
1565 /*}}}*/
1566 // FileFd::Tell - Current seek position /*{{{*/
1567 // ---------------------------------------------------------------------
1568 /* */
1569 unsigned long long FileFd::Tell()
1570 {
1571 // In theory, we could just return seekpos here always instead of
1572 // seeking around, but not all users of FileFd use always Seek() and co
1573 // so d->seekpos isn't always true and we can just use it as a hint if
1574 // we have nothing else, but not always as an authority…
1575 if (d != NULL && (d->pipe == true
1576 #ifdef HAVE_BZ2
1577 || d->bz2 != NULL
1578 #endif
1579 ))
1580 return d->seekpos;
1581
1582 off_t Res;
1583 #ifdef HAVE_ZLIB
1584 if (d != NULL && d->gz != NULL)
1585 Res = gztell(d->gz);
1586 else
1587 #endif
1588 Res = lseek(iFd,0,SEEK_CUR);
1589 if (Res == (off_t)-1)
1590 FileFdErrno("lseek","Failed to determine the current file position");
1591 if (d != NULL)
1592 d->seekpos = Res;
1593 return Res;
1594 }
1595 /*}}}*/
1596 static bool StatFileFd(char const * const msg, int const iFd, std::string const &FileName, struct stat &Buf, FileFdPrivate * const d) /*{{{*/
1597 {
1598 bool ispipe = (d != NULL && d->pipe == true);
1599 if (ispipe == false)
1600 {
1601 if (fstat(iFd,&Buf) != 0)
1602 // higher-level code will generate more meaningful messages,
1603 // even translated this would be meaningless for users
1604 return _error->Errno("fstat", "Unable to determine %s for fd %i", msg, iFd);
1605 ispipe = S_ISFIFO(Buf.st_mode);
1606 }
1607
1608 // for compressor pipes st_size is undefined and at 'best' zero
1609 if (ispipe == true)
1610 {
1611 // we set it here, too, as we get the info here for free
1612 // in theory the Open-methods should take care of it already
1613 if (d != NULL)
1614 d->pipe = true;
1615 if (stat(FileName.c_str(), &Buf) != 0)
1616 return _error->Errno("fstat", "Unable to determine %s for file %s", msg, FileName.c_str());
1617 }
1618 return true;
1619 }
1620 /*}}}*/
1621 // FileFd::FileSize - Return the size of the file /*{{{*/
1622 unsigned long long FileFd::FileSize()
1623 {
1624 struct stat Buf;
1625 if (StatFileFd("file size", iFd, FileName, Buf, d) == false)
1626 {
1627 Flags |= Fail;
1628 return 0;
1629 }
1630 return Buf.st_size;
1631 }
1632 /*}}}*/
1633 // FileFd::ModificationTime - Return the time of last touch /*{{{*/
1634 time_t FileFd::ModificationTime()
1635 {
1636 struct stat Buf;
1637 if (StatFileFd("modification time", iFd, FileName, Buf, d) == false)
1638 {
1639 Flags |= Fail;
1640 return 0;
1641 }
1642 return Buf.st_mtime;
1643 }
1644 /*}}}*/
1645 // FileFd::Size - Return the size of the content in the file /*{{{*/
1646 // ---------------------------------------------------------------------
1647 /* */
1648 unsigned long long FileFd::Size()
1649 {
1650 unsigned long long size = FileSize();
1651
1652 // for compressor pipes st_size is undefined and at 'best' zero,
1653 // so we 'read' the content and 'seek' back - see there
1654 if (d != NULL && (d->pipe == true
1655 #ifdef HAVE_BZ2
1656 || (d->bz2 && size > 0)
1657 #endif
1658 ))
1659 {
1660 unsigned long long const oldSeek = Tell();
1661 char ignore[1000];
1662 unsigned long long read = 0;
1663 do {
1664 if (Read(ignore, sizeof(ignore), &read) == false)
1665 {
1666 Seek(oldSeek);
1667 return 0;
1668 }
1669 } while(read != 0);
1670 size = Tell();
1671 Seek(oldSeek);
1672 }
1673 #ifdef HAVE_ZLIB
1674 // only check gzsize if we are actually a gzip file, just checking for
1675 // "gz" is not sufficient as uncompressed files could be opened with
1676 // gzopen in "direct" mode as well
1677 else if (d != NULL && d->gz && !gzdirect(d->gz) && size > 0)
1678 {
1679 off_t const oldPos = lseek(iFd,0,SEEK_CUR);
1680 /* unfortunately zlib.h doesn't provide a gzsize(), so we have to do
1681 * this ourselves; the original (uncompressed) file size is the last 32
1682 * bits of the file */
1683 // FIXME: Size for gz-files is limited by 32bit… no largefile support
1684 if (lseek(iFd, -4, SEEK_END) < 0)
1685 {
1686 FileFdErrno("lseek","Unable to seek to end of gzipped file");
1687 return 0;
1688 }
1689 size = 0;
1690 if (read(iFd, &size, 4) != 4)
1691 {
1692 FileFdErrno("read","Unable to read original size of gzipped file");
1693 return 0;
1694 }
1695
1696 #ifdef WORDS_BIGENDIAN
1697 uint32_t tmp_size = size;
1698 uint8_t const * const p = (uint8_t const * const) &tmp_size;
1699 tmp_size = (p[3] << 24) | (p[2] << 16) | (p[1] << 8) | p[0];
1700 size = tmp_size;
1701 #endif
1702
1703 if (lseek(iFd, oldPos, SEEK_SET) < 0)
1704 {
1705 FileFdErrno("lseek","Unable to seek in gzipped file");
1706 return 0;
1707 }
1708
1709 return size;
1710 }
1711 #endif
1712
1713 return size;
1714 }
1715 /*}}}*/
1716 // FileFd::Close - Close the file if the close flag is set /*{{{*/
1717 // ---------------------------------------------------------------------
1718 /* */
1719 bool FileFd::Close()
1720 {
1721 if (iFd == -1)
1722 return true;
1723
1724 bool Res = true;
1725 if ((Flags & AutoClose) == AutoClose)
1726 {
1727 if ((Flags & Compressed) != Compressed && iFd > 0 && close(iFd) != 0)
1728 Res &= _error->Errno("close",_("Problem closing the file %s"), FileName.c_str());
1729
1730 if (d != NULL)
1731 {
1732 Res &= d->CloseDown(FileName);
1733 delete d;
1734 d = NULL;
1735 }
1736 }
1737
1738 if ((Flags & Replace) == Replace) {
1739 if (rename(TemporaryFileName.c_str(), FileName.c_str()) != 0)
1740 Res &= _error->Errno("rename",_("Problem renaming the file %s to %s"), TemporaryFileName.c_str(), FileName.c_str());
1741
1742 FileName = TemporaryFileName; // for the unlink() below.
1743 TemporaryFileName.clear();
1744 }
1745
1746 iFd = -1;
1747
1748 if ((Flags & Fail) == Fail && (Flags & DelOnFail) == DelOnFail &&
1749 FileName.empty() == false)
1750 if (unlink(FileName.c_str()) != 0)
1751 Res &= _error->WarningE("unlnk",_("Problem unlinking the file %s"), FileName.c_str());
1752
1753 if (Res == false)
1754 Flags |= Fail;
1755 return Res;
1756 }
1757 /*}}}*/
1758 // FileFd::Sync - Sync the file /*{{{*/
1759 // ---------------------------------------------------------------------
1760 /* */
1761 bool FileFd::Sync()
1762 {
1763 if (fsync(iFd) != 0)
1764 return FileFdErrno("sync",_("Problem syncing the file"));
1765 return true;
1766 }
1767 /*}}}*/
1768 // FileFd::FileFdErrno - set Fail and call _error->Errno *{{{*/
1769 bool FileFd::FileFdErrno(const char *Function, const char *Description,...)
1770 {
1771 Flags |= Fail;
1772 va_list args;
1773 size_t msgSize = 400;
1774 int const errsv = errno;
1775 while (true)
1776 {
1777 va_start(args,Description);
1778 if (_error->InsertErrno(GlobalError::ERROR, Function, Description, args, errsv, msgSize) == false)
1779 break;
1780 va_end(args);
1781 }
1782 return false;
1783 }
1784 /*}}}*/
1785 // FileFd::FileFdError - set Fail and call _error->Error *{{{*/
1786 bool FileFd::FileFdError(const char *Description,...) {
1787 Flags |= Fail;
1788 va_list args;
1789 size_t msgSize = 400;
1790 while (true)
1791 {
1792 va_start(args,Description);
1793 if (_error->Insert(GlobalError::ERROR, Description, args, msgSize) == false)
1794 break;
1795 va_end(args);
1796 }
1797 return false;
1798 }
1799 /*}}}*/
1800
1801 gzFile FileFd::gzFd() { return d->gz; }
1802
1803
1804 // Glob - wrapper around "glob()" /*{{{*/
1805 // ---------------------------------------------------------------------
1806 /* */
1807 std::vector<std::string> Glob(std::string const &pattern, int flags)
1808 {
1809 std::vector<std::string> result;
1810 glob_t globbuf;
1811 int glob_res;
1812 unsigned int i;
1813
1814 glob_res = glob(pattern.c_str(), flags, NULL, &globbuf);
1815
1816 if (glob_res != 0)
1817 {
1818 if(glob_res != GLOB_NOMATCH) {
1819 _error->Errno("glob", "Problem with glob");
1820 return result;
1821 }
1822 }
1823
1824 // append results
1825 for(i=0;i<globbuf.gl_pathc;i++)
1826 result.push_back(string(globbuf.gl_pathv[i]));
1827
1828 globfree(&globbuf);
1829 return result;
1830 }
1831 /*}}}*/
1832
1833 std::string GetTempDir()
1834 {
1835 const char *tmpdir = getenv("TMPDIR");
1836
1837 #ifdef P_tmpdir
1838 if (!tmpdir)
1839 tmpdir = P_tmpdir;
1840 #endif
1841
1842 // check that tmpdir is set and exists
1843 struct stat st;
1844 if (!tmpdir || strlen(tmpdir) == 0 || stat(tmpdir, &st) != 0)
1845 tmpdir = "/tmp";
1846
1847 return string(tmpdir);
1848 }