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