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