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