1 // -*- mode: cpp; mode: fold -*-
3 // $Id: fileutl.cc,v 1.42 2002/09/14 05:29:22 jgg Exp $
4 /* ######################################################################
8 CopyFile - Buffered copy of a single file
9 GetLock - dpkg compatible lock file manipulation (fcntl)
11 Most of this source is placed in the Public Domain, do with it what
13 It was originally written by Jason Gunthorpe <jgg@debian.org>.
14 FileFd gzip support added by Martin Pitt <martin.pitt@canonical.com>
16 The exception is RunScripts() it is under the GPLv2
18 ##################################################################### */
20 // Include Files /*{{{*/
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>
38 #include <sys/types.h>
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
54 #pragma message "Usage of zlib is DISABLED!"
57 #ifdef WORDS_BIGENDIAN
76 APT::Configuration::Compressor compressor
;
77 unsigned int openmode
;
78 FileFdPrivate() : gz(NULL
), compressed_fd(-1), compressor_pid(-1), pipe(false) {};
81 // RunScripts - Run a set of scripts from a configuration subtree /*{{{*/
82 // ---------------------------------------------------------------------
84 bool RunScripts(const char *Cnf
)
86 Configuration::Item
const *Opts
= _config
->Tree(Cnf
);
87 if (Opts
== 0 || Opts
->Child
== 0)
91 // Fork for running the system calls
92 pid_t Child
= ExecFork();
97 if (_config
->FindDir("DPkg::Chroot-Directory","/") != "/")
99 std::cerr
<< "Chrooting into "
100 << _config
->FindDir("DPkg::Chroot-Directory")
102 if (chroot(_config
->FindDir("DPkg::Chroot-Directory","/").c_str()) != 0)
106 if (chdir("/tmp/") != 0)
109 unsigned int Count
= 1;
110 for (; Opts
!= 0; Opts
= Opts
->Next
, Count
++)
112 if (Opts
->Value
.empty() == true)
115 if (system(Opts
->Value
.c_str()) != 0)
121 // Wait for the child
123 while (waitpid(Child
,&Status
,0) != Child
)
127 return _error
->Errno("waitpid","Couldn't wait for subprocess");
130 // Restore sig int/quit
131 signal(SIGQUIT
,SIG_DFL
);
132 signal(SIGINT
,SIG_DFL
);
134 // Check for an error code.
135 if (WIFEXITED(Status
) == 0 || WEXITSTATUS(Status
) != 0)
137 unsigned int Count
= WEXITSTATUS(Status
);
141 for (; Opts
!= 0 && Count
!= 1; Opts
= Opts
->Next
, Count
--);
142 _error
->Error("Problem executing scripts %s '%s'",Cnf
,Opts
->Value
.c_str());
145 return _error
->Error("Sub-process returned an error code");
152 // CopyFile - Buffered copy of a file /*{{{*/
153 // ---------------------------------------------------------------------
154 /* The caller is expected to set things so that failure causes erasure */
155 bool CopyFile(FileFd
&From
,FileFd
&To
)
157 if (From
.IsOpen() == false || To
.IsOpen() == false)
160 // Buffered copy between fds
161 SPtrArray
<unsigned char> Buf
= new unsigned char[64000];
162 unsigned long long Size
= From
.Size();
165 unsigned long long ToRead
= Size
;
169 if (From
.Read(Buf
,ToRead
) == false ||
170 To
.Write(Buf
,ToRead
) == false)
179 // GetLock - Gets a lock file /*{{{*/
180 // ---------------------------------------------------------------------
181 /* This will create an empty file of the given name and lock it. Once this
182 is done all other calls to GetLock in any other process will fail with
183 -1. The return result is the fd of the file, the call should call
184 close at some time. */
185 int GetLock(string File
,bool Errors
)
187 // GetLock() is used in aptitude on directories with public-write access
188 // Use O_NOFOLLOW here to prevent symlink traversal attacks
189 int FD
= open(File
.c_str(),O_RDWR
| O_CREAT
| O_NOFOLLOW
,0640);
192 // Read only .. cant have locking problems there.
195 _error
->Warning(_("Not using locking for read only lock file %s"),File
.c_str());
196 return dup(0); // Need something for the caller to close
200 _error
->Errno("open",_("Could not open lock file %s"),File
.c_str());
202 // Feh.. We do this to distinguish the lock vs open case..
206 SetCloseExec(FD
,true);
208 // Aquire a write lock
211 fl
.l_whence
= SEEK_SET
;
214 if (fcntl(FD
,F_SETLK
,&fl
) == -1)
218 _error
->Warning(_("Not using locking for nfs mounted lock file %s"),File
.c_str());
219 return dup(0); // Need something for the caller to close
222 _error
->Errno("open",_("Could not get lock %s"),File
.c_str());
233 // FileExists - Check if a file exists /*{{{*/
234 // ---------------------------------------------------------------------
235 /* Beware: Directories are also files! */
236 bool FileExists(string File
)
239 if (stat(File
.c_str(),&Buf
) != 0)
244 // RealFileExists - Check if a file exists and if it is really a file /*{{{*/
245 // ---------------------------------------------------------------------
247 bool RealFileExists(string File
)
250 if (stat(File
.c_str(),&Buf
) != 0)
252 return ((Buf
.st_mode
& S_IFREG
) != 0);
255 // DirectoryExists - Check if a directory exists and is really one /*{{{*/
256 // ---------------------------------------------------------------------
258 bool DirectoryExists(string
const &Path
)
261 if (stat(Path
.c_str(),&Buf
) != 0)
263 return ((Buf
.st_mode
& S_IFDIR
) != 0);
266 // CreateDirectory - poor man's mkdir -p guarded by a parent directory /*{{{*/
267 // ---------------------------------------------------------------------
268 /* This method will create all directories needed for path in good old
269 mkdir -p style but refuses to do this if Parent is not a prefix of
270 this Path. Example: /var/cache/ and /var/cache/apt/archives are given,
271 so it will create apt/archives if /var/cache exists - on the other
272 hand if the parent is /var/lib the creation will fail as this path
273 is not a parent of the path to be generated. */
274 bool CreateDirectory(string
const &Parent
, string
const &Path
)
276 if (Parent
.empty() == true || Path
.empty() == true)
279 if (DirectoryExists(Path
) == true)
282 if (DirectoryExists(Parent
) == false)
285 // we are not going to create directories "into the blue"
286 if (Path
.find(Parent
, 0) != 0)
289 vector
<string
> const dirs
= VectorizeString(Path
.substr(Parent
.size()), '/');
290 string progress
= Parent
;
291 for (vector
<string
>::const_iterator d
= dirs
.begin(); d
!= dirs
.end(); ++d
)
293 if (d
->empty() == true)
296 progress
.append("/").append(*d
);
297 if (DirectoryExists(progress
) == true)
300 if (mkdir(progress
.c_str(), 0755) != 0)
306 // CreateAPTDirectoryIfNeeded - ensure that the given directory exists /*{{{*/
307 // ---------------------------------------------------------------------
308 /* a small wrapper around CreateDirectory to check if it exists and to
309 remove the trailing "/apt/" from the parent directory if needed */
310 bool CreateAPTDirectoryIfNeeded(string
const &Parent
, string
const &Path
)
312 if (DirectoryExists(Path
) == true)
315 size_t const len
= Parent
.size();
316 if (len
> 5 && Parent
.find("/apt/", len
- 6, 5) == len
- 5)
318 if (CreateDirectory(Parent
.substr(0,len
-5), Path
) == true)
321 else if (CreateDirectory(Parent
, Path
) == true)
327 // GetListOfFilesInDir - returns a vector of files in the given dir /*{{{*/
328 // ---------------------------------------------------------------------
329 /* If an extension is given only files with this extension are included
330 in the returned vector, otherwise every "normal" file is included. */
331 std::vector
<string
> GetListOfFilesInDir(string
const &Dir
, string
const &Ext
,
332 bool const &SortList
, bool const &AllowNoExt
)
334 std::vector
<string
> ext
;
336 if (Ext
.empty() == false)
338 if (AllowNoExt
== true && ext
.empty() == false)
340 return GetListOfFilesInDir(Dir
, ext
, SortList
);
342 std::vector
<string
> GetListOfFilesInDir(string
const &Dir
, std::vector
<string
> const &Ext
,
343 bool const &SortList
)
345 // Attention debuggers: need to be set with the environment config file!
346 bool const Debug
= _config
->FindB("Debug::GetListOfFilesInDir", false);
349 std::clog
<< "Accept in " << Dir
<< " only files with the following " << Ext
.size() << " extensions:" << std::endl
;
350 if (Ext
.empty() == true)
351 std::clog
<< "\tNO extension" << std::endl
;
353 for (std::vector
<string
>::const_iterator e
= Ext
.begin();
355 std::clog
<< '\t' << (e
->empty() == true ? "NO" : *e
) << " extension" << std::endl
;
358 std::vector
<string
> List
;
360 if (DirectoryExists(Dir
.c_str()) == false)
362 _error
->Error(_("List of files can't be created as '%s' is not a directory"), Dir
.c_str());
366 Configuration::MatchAgainstConfig
SilentIgnore("Dir::Ignore-Files-Silently");
367 DIR *D
= opendir(Dir
.c_str());
370 _error
->Errno("opendir",_("Unable to read %s"),Dir
.c_str());
374 for (struct dirent
*Ent
= readdir(D
); Ent
!= 0; Ent
= readdir(D
))
376 // skip "hidden" files
377 if (Ent
->d_name
[0] == '.')
380 // Make sure it is a file and not something else
381 string
const File
= flCombine(Dir
,Ent
->d_name
);
382 #ifdef _DIRENT_HAVE_D_TYPE
383 if (Ent
->d_type
!= DT_REG
)
386 if (RealFileExists(File
.c_str()) == false)
388 if (SilentIgnore
.Match(Ent
->d_name
) == false)
389 _error
->Notice(_("Ignoring '%s' in directory '%s' as it is not a regular file"), Ent
->d_name
, Dir
.c_str());
394 // check for accepted extension:
395 // no extension given -> periods are bad as hell!
396 // extensions given -> "" extension allows no extension
397 if (Ext
.empty() == false)
399 string d_ext
= flExtension(Ent
->d_name
);
400 if (d_ext
== Ent
->d_name
) // no extension
402 if (std::find(Ext
.begin(), Ext
.end(), "") == Ext
.end())
405 std::clog
<< "Bad file: " << Ent
->d_name
<< " → no extension" << std::endl
;
406 if (SilentIgnore
.Match(Ent
->d_name
) == false)
407 _error
->Notice(_("Ignoring file '%s' in directory '%s' as it has no filename extension"), Ent
->d_name
, Dir
.c_str());
411 else if (std::find(Ext
.begin(), Ext
.end(), d_ext
) == Ext
.end())
414 std::clog
<< "Bad file: " << Ent
->d_name
<< " → bad extension »" << flExtension(Ent
->d_name
) << "«" << std::endl
;
415 if (SilentIgnore
.Match(Ent
->d_name
) == false)
416 _error
->Notice(_("Ignoring file '%s' in directory '%s' as it has an invalid filename extension"), Ent
->d_name
, Dir
.c_str());
421 // Skip bad filenames ala run-parts
422 const char *C
= Ent
->d_name
;
424 if (isalpha(*C
) == 0 && isdigit(*C
) == 0
425 && *C
!= '_' && *C
!= '-') {
426 // no required extension -> dot is a bad character
427 if (*C
== '.' && Ext
.empty() == false)
432 // we don't reach the end of the name -> bad character included
436 std::clog
<< "Bad file: " << Ent
->d_name
<< " → bad character »"
437 << *C
<< "« in filename (period allowed: " << (Ext
.empty() ? "no" : "yes") << ")" << std::endl
;
441 // skip filenames which end with a period. These are never valid
445 std::clog
<< "Bad file: " << Ent
->d_name
<< " → Period as last character" << std::endl
;
450 std::clog
<< "Accept file: " << Ent
->d_name
<< " in " << Dir
<< std::endl
;
451 List
.push_back(File
);
455 if (SortList
== true)
456 std::sort(List
.begin(),List
.end());
460 // SafeGetCWD - This is a safer getcwd that returns a dynamic string /*{{{*/
461 // ---------------------------------------------------------------------
462 /* We return / on failure. */
465 // Stash the current dir.
468 if (getcwd(S
,sizeof(S
)-2) == 0)
470 unsigned int Len
= strlen(S
);
476 // GetModificationTime - Get the mtime of the given file or -1 on error /*{{{*/
477 // ---------------------------------------------------------------------
478 /* We return / on failure. */
479 time_t GetModificationTime(string
const &Path
)
482 if (stat(Path
.c_str(), &St
) < 0)
487 // flNotDir - Strip the directory from the filename /*{{{*/
488 // ---------------------------------------------------------------------
490 string
flNotDir(string File
)
492 string::size_type Res
= File
.rfind('/');
493 if (Res
== string::npos
)
496 return string(File
,Res
,Res
- File
.length());
499 // flNotFile - Strip the file from the directory name /*{{{*/
500 // ---------------------------------------------------------------------
501 /* Result ends in a / */
502 string
flNotFile(string File
)
504 string::size_type Res
= File
.rfind('/');
505 if (Res
== string::npos
)
508 return string(File
,0,Res
);
511 // flExtension - Return the extension for the file /*{{{*/
512 // ---------------------------------------------------------------------
514 string
flExtension(string File
)
516 string::size_type Res
= File
.rfind('.');
517 if (Res
== string::npos
)
520 return string(File
,Res
,Res
- File
.length());
523 // flNoLink - If file is a symlink then deref it /*{{{*/
524 // ---------------------------------------------------------------------
525 /* If the name is not a link then the returned path is the input. */
526 string
flNoLink(string File
)
529 if (lstat(File
.c_str(),&St
) != 0 || S_ISLNK(St
.st_mode
) == 0)
531 if (stat(File
.c_str(),&St
) != 0)
534 /* Loop resolving the link. There is no need to limit the number of
535 loops because the stat call above ensures that the symlink is not
543 if ((Res
= readlink(NFile
.c_str(),Buffer
,sizeof(Buffer
))) <= 0 ||
544 (unsigned)Res
>= sizeof(Buffer
))
547 // Append or replace the previous path
549 if (Buffer
[0] == '/')
552 NFile
= flNotFile(NFile
) + Buffer
;
554 // See if we are done
555 if (lstat(NFile
.c_str(),&St
) != 0)
557 if (S_ISLNK(St
.st_mode
) == 0)
562 // flCombine - Combine a file and a directory /*{{{*/
563 // ---------------------------------------------------------------------
564 /* If the file is an absolute path then it is just returned, otherwise
565 the directory is pre-pended to it. */
566 string
flCombine(string Dir
,string File
)
568 if (File
.empty() == true)
571 if (File
[0] == '/' || Dir
.empty() == true)
573 if (File
.length() >= 2 && File
[0] == '.' && File
[1] == '/')
575 if (Dir
[Dir
.length()-1] == '/')
577 return Dir
+ '/' + File
;
580 // SetCloseExec - Set the close on exec flag /*{{{*/
581 // ---------------------------------------------------------------------
583 void SetCloseExec(int Fd
,bool Close
)
585 if (fcntl(Fd
,F_SETFD
,(Close
== false)?0:FD_CLOEXEC
) != 0)
587 cerr
<< "FATAL -> Could not set close on exec " << strerror(errno
) << endl
;
592 // SetNonBlock - Set the nonblocking flag /*{{{*/
593 // ---------------------------------------------------------------------
595 void SetNonBlock(int Fd
,bool Block
)
597 int Flags
= fcntl(Fd
,F_GETFL
) & (~O_NONBLOCK
);
598 if (fcntl(Fd
,F_SETFL
,Flags
| ((Block
== false)?0:O_NONBLOCK
)) != 0)
600 cerr
<< "FATAL -> Could not set non-blocking flag " << strerror(errno
) << endl
;
605 // WaitFd - Wait for a FD to become readable /*{{{*/
606 // ---------------------------------------------------------------------
607 /* This waits for a FD to become readable using select. It is useful for
608 applications making use of non-blocking sockets. The timeout is
610 bool WaitFd(int Fd
,bool write
,unsigned long timeout
)
623 Res
= select(Fd
+1,0,&Set
,0,(timeout
!= 0?&tv
:0));
625 while (Res
< 0 && errno
== EINTR
);
635 Res
= select(Fd
+1,&Set
,0,0,(timeout
!= 0?&tv
:0));
637 while (Res
< 0 && errno
== EINTR
);
646 // ExecFork - Magical fork that sanitizes the context before execing /*{{{*/
647 // ---------------------------------------------------------------------
648 /* This is used if you want to cleanse the environment for the forked
649 child, it fixes up the important signals and nukes all of the fds,
650 otherwise acts like normal fork. */
653 // Fork off the process
654 pid_t Process
= fork();
657 cerr
<< "FATAL -> Failed to fork." << endl
;
661 // Spawn the subprocess
665 signal(SIGPIPE
,SIG_DFL
);
666 signal(SIGQUIT
,SIG_DFL
);
667 signal(SIGINT
,SIG_DFL
);
668 signal(SIGWINCH
,SIG_DFL
);
669 signal(SIGCONT
,SIG_DFL
);
670 signal(SIGTSTP
,SIG_DFL
);
673 Configuration::Item
const *Opts
= _config
->Tree("APT::Keep-Fds");
674 if (Opts
!= 0 && Opts
->Child
!= 0)
677 for (; Opts
!= 0; Opts
= Opts
->Next
)
679 if (Opts
->Value
.empty() == true)
681 int fd
= atoi(Opts
->Value
.c_str());
686 // Close all of our FDs - just in case
687 for (int K
= 3; K
!= 40; K
++)
689 if(KeepFDs
.find(K
) == KeepFDs
.end())
690 fcntl(K
,F_SETFD
,FD_CLOEXEC
);
697 // ExecWait - Fancy waitpid /*{{{*/
698 // ---------------------------------------------------------------------
699 /* Waits for the given sub process. If Reap is set then no errors are
700 generated. Otherwise a failed subprocess will generate a proper descriptive
702 bool ExecWait(pid_t Pid
,const char *Name
,bool Reap
)
707 // Wait and collect the error code
709 while (waitpid(Pid
,&Status
,0) != Pid
)
717 return _error
->Error(_("Waited for %s but it wasn't there"),Name
);
721 // Check for an error code.
722 if (WIFEXITED(Status
) == 0 || WEXITSTATUS(Status
) != 0)
726 if (WIFSIGNALED(Status
) != 0)
728 if( WTERMSIG(Status
) == SIGSEGV
)
729 return _error
->Error(_("Sub-process %s received a segmentation fault."),Name
);
731 return _error
->Error(_("Sub-process %s received signal %u."),Name
, WTERMSIG(Status
));
734 if (WIFEXITED(Status
) != 0)
735 return _error
->Error(_("Sub-process %s returned an error code (%u)"),Name
,WEXITSTATUS(Status
));
737 return _error
->Error(_("Sub-process %s exited unexpectedly"),Name
);
744 // FileFd::Open - Open a file /*{{{*/
745 // ---------------------------------------------------------------------
746 /* The most commonly used open mode combinations are given with Mode */
747 bool FileFd::Open(string FileName
,unsigned int const Mode
,CompressMode Compress
, unsigned long const Perms
)
749 if (Mode
== ReadOnlyGzip
)
750 return Open(FileName
, ReadOnly
, Gzip
, Perms
);
752 if (Compress
== Auto
&& (Mode
& WriteOnly
) == WriteOnly
)
753 return _error
->Error("Autodetection on %s only works in ReadOnly openmode!", FileName
.c_str());
755 // FIXME: Denote inbuilt compressors somehow - as we don't need to have the binaries for them
756 std::vector
<APT::Configuration::Compressor
> const compressors
= APT::Configuration::getCompressors();
757 std::vector
<APT::Configuration::Compressor
>::const_iterator compressor
= compressors
.begin();
758 if (Compress
== Auto
)
760 for (; compressor
!= compressors
.end(); ++compressor
)
762 std::string file
= std::string(FileName
).append(compressor
->Extension
);
763 if (FileExists(file
) == false)
769 else if (Compress
== Extension
)
771 std::string::size_type
const found
= FileName
.find_last_of('.');
773 if (found
!= std::string::npos
)
775 ext
= FileName
.substr(found
);
776 if (ext
== ".new" || ext
== ".bak")
778 std::string::size_type
const found2
= FileName
.find_last_of('.', found
- 1);
779 if (found2
!= std::string::npos
)
780 ext
= FileName
.substr(found2
, found
- found2
);
785 for (; compressor
!= compressors
.end(); ++compressor
)
786 if (ext
== compressor
->Extension
)
788 // no matching extension - assume uncompressed (imagine files like 'example.org_Packages')
789 if (compressor
== compressors
.end())
790 for (compressor
= compressors
.begin(); compressor
!= compressors
.end(); ++compressor
)
791 if (compressor
->Name
== ".")
799 case None
: name
= "."; break;
800 case Gzip
: name
= "gzip"; break;
801 case Bzip2
: name
= "bzip2"; break;
802 case Lzma
: name
= "lzma"; break;
803 case Xz
: name
= "xz"; break;
807 return _error
->Error("Opening File %s in None, Auto or Extension should be already handled?!?", FileName
.c_str());
809 for (; compressor
!= compressors
.end(); ++compressor
)
810 if (compressor
->Name
== name
)
812 if (compressor
== compressors
.end())
813 return _error
->Error("Can't find a configured compressor %s for file %s", name
.c_str(), FileName
.c_str());
816 if (compressor
== compressors
.end())
817 return _error
->Error("Can't find a match for specified compressor mode for file %s", FileName
.c_str());
818 return Open(FileName
, Mode
, *compressor
, Perms
);
820 bool FileFd::Open(string FileName
,unsigned int const Mode
,APT::Configuration::Compressor
const &compressor
, unsigned long const Perms
)
823 d
= new FileFdPrivate
;
827 if ((Mode
& WriteOnly
) != WriteOnly
&& (Mode
& (Atomic
| Create
| Empty
| Exclusive
)) != 0)
828 return _error
->Error("ReadOnly mode for %s doesn't accept additional flags!", FileName
.c_str());
829 if ((Mode
& ReadWrite
) == 0)
830 return _error
->Error("No openmode provided in FileFd::Open for %s", FileName
.c_str());
832 if ((Mode
& Atomic
) == Atomic
)
835 char *name
= strdup((FileName
+ ".XXXXXX").c_str());
836 TemporaryFileName
= string(mktemp(name
));
839 else if ((Mode
& (Exclusive
| Create
)) == (Exclusive
| Create
))
841 // for atomic, this will be done by rename in Close()
842 unlink(FileName
.c_str());
844 if ((Mode
& Empty
) == Empty
)
847 if (lstat(FileName
.c_str(),&Buf
) == 0 && S_ISLNK(Buf
.st_mode
))
848 unlink(FileName
.c_str());
852 #define if_FLAGGED_SET(FLAG, MODE) if ((Mode & FLAG) == FLAG) fileflags |= MODE
853 if_FLAGGED_SET(ReadWrite
, O_RDWR
);
854 else if_FLAGGED_SET(ReadOnly
, O_RDONLY
);
855 else if_FLAGGED_SET(WriteOnly
, O_WRONLY
);
857 if_FLAGGED_SET(Create
, O_CREAT
);
858 if_FLAGGED_SET(Empty
, O_TRUNC
);
859 if_FLAGGED_SET(Exclusive
, O_EXCL
);
860 else if_FLAGGED_SET(Atomic
, O_EXCL
);
861 #undef if_FLAGGED_SET
863 if (TemporaryFileName
.empty() == false)
864 iFd
= open(TemporaryFileName
.c_str(), fileflags
, Perms
);
866 iFd
= open(FileName
.c_str(), fileflags
, Perms
);
868 if (iFd
== -1 || OpenInternDescriptor(Mode
, compressor
) == false)
875 return _error
->Errno("open",_("Could not open file %s"), FileName
.c_str());
878 this->FileName
= FileName
;
879 SetCloseExec(iFd
,true);
883 // FileFd::OpenDescriptor - Open a filedescriptor /*{{{*/
884 // ---------------------------------------------------------------------
886 bool FileFd::OpenDescriptor(int Fd
, unsigned int const Mode
, CompressMode Compress
, bool AutoClose
)
888 std::vector
<APT::Configuration::Compressor
> const compressors
= APT::Configuration::getCompressors();
889 std::vector
<APT::Configuration::Compressor
>::const_iterator compressor
= compressors
.begin();
893 case None
: name
= "."; break;
894 case Gzip
: name
= "gzip"; break;
895 case Bzip2
: name
= "bzip2"; break;
896 case Lzma
: name
= "lzma"; break;
897 case Xz
: name
= "xz"; break;
900 return _error
->Error("Opening Fd %d in Auto or Extension compression mode is not supported", Fd
);
902 for (; compressor
!= compressors
.end(); ++compressor
)
903 if (compressor
->Name
== name
)
905 if (compressor
== compressors
.end())
906 return _error
->Error("Can't find a configured compressor %s for file %s", name
.c_str(), FileName
.c_str());
908 return OpenDescriptor(Fd
, Mode
, *compressor
, AutoClose
);
910 bool FileFd::OpenDescriptor(int Fd
, unsigned int const Mode
, APT::Configuration::Compressor
const &compressor
, bool AutoClose
)
913 d
= new FileFdPrivate
;
915 Flags
= (AutoClose
) ? FileFd::AutoClose
: 0;
917 if (OpenInternDescriptor(Mode
, compressor
) == false)
921 return _error
->Errno("gzdopen",_("Could not open file descriptor %d"), Fd
);
926 bool FileFd::OpenInternDescriptor(unsigned int const Mode
, APT::Configuration::Compressor
const &compressor
)
928 d
->compressor
= compressor
;
929 if (compressor
.Name
== "." || compressor
.Binary
.empty() == true)
932 else if (compressor
.Name
== "gzip")
934 if ((Mode
& ReadWrite
) == ReadWrite
)
935 d
->gz
= gzdopen(iFd
, "r+");
936 else if ((Mode
& WriteOnly
) == WriteOnly
)
937 d
->gz
= gzdopen(iFd
, "w");
939 d
->gz
= gzdopen (iFd
, "r");
947 if ((Mode
& ReadWrite
) == ReadWrite
)
948 return _error
->Error("ReadWrite mode is not supported for file %s", FileName
.c_str());
950 bool const Comp
= (Mode
& WriteOnly
) == WriteOnly
;
951 // Handle 'decompression' of empty files
956 if (Buf
.st_size
== 0 && S_ISFIFO(Buf
.st_mode
) == false)
959 // We don't need the file open - instead let the compressor open it
960 // as he properly knows better how to efficiently read from 'his' file
961 if (FileName
.empty() == false)
965 // Create a data pipe
966 int Pipe
[2] = {-1,-1};
968 return _error
->Errno("pipe",_("Failed to create subprocess IPC"));
969 for (int J
= 0; J
!= 2; J
++)
970 SetCloseExec(Pipe
[J
],true);
972 d
->compressed_fd
= iFd
;
981 d
->compressor_pid
= ExecFork();
982 if (d
->compressor_pid
== 0)
986 dup2(d
->compressed_fd
,STDOUT_FILENO
);
987 dup2(Pipe
[0],STDIN_FILENO
);
991 if (FileName
.empty() == true)
992 dup2(d
->compressed_fd
,STDIN_FILENO
);
993 dup2(Pipe
[1],STDOUT_FILENO
);
996 SetCloseExec(STDOUT_FILENO
,false);
997 SetCloseExec(STDIN_FILENO
,false);
999 std::vector
<char const*> Args
;
1000 Args
.push_back(compressor
.Binary
.c_str());
1001 std::vector
<std::string
> const * const addArgs
=
1002 (Comp
== true) ? &(compressor
.CompressArgs
) : &(compressor
.UncompressArgs
);
1003 for (std::vector
<std::string
>::const_iterator a
= addArgs
->begin();
1004 a
!= addArgs
->end(); ++a
)
1005 Args
.push_back(a
->c_str());
1006 if (Comp
== false && FileName
.empty() == false)
1008 Args
.push_back("--stdout");
1009 if (TemporaryFileName
.empty() == false)
1010 Args
.push_back(TemporaryFileName
.c_str());
1012 Args
.push_back(FileName
.c_str());
1014 Args
.push_back(NULL
);
1016 execvp(Args
[0],(char **)&Args
[0]);
1017 cerr
<< _("Failed to exec compressor ") << Args
[0] << endl
;
1024 if (Comp
== true || FileName
.empty() == true)
1025 close(d
->compressed_fd
);
1030 // FileFd::~File - Closes the file /*{{{*/
1031 // ---------------------------------------------------------------------
1032 /* If the proper modes are selected then we close the Fd and possibly
1033 unlink the file on error. */
1039 // FileFd::Read - Read a bit of the file /*{{{*/
1040 // ---------------------------------------------------------------------
1041 /* We are carefull to handle interruption by a signal while reading
1043 bool FileFd::Read(void *To
,unsigned long long Size
,unsigned long long *Actual
)
1049 *((char *)To
) = '\0';
1054 Res
= gzread(d
->gz
,To
,Size
);
1057 Res
= read(iFd
,To
,Size
);
1058 if (Res
< 0 && errno
== EINTR
)
1063 return _error
->Errno("read",_("Read error"));
1066 To
= (char *)To
+ Res
;
1071 while (Res
> 0 && Size
> 0);
1084 return _error
->Error(_("read, still have %llu to read but none left"), Size
);
1087 // FileFd::ReadLine - Read a complete line from the file /*{{{*/
1088 // ---------------------------------------------------------------------
1089 /* Beware: This method can be quiet slow for big buffers on UNcompressed
1090 files because of the naive implementation! */
1091 char* FileFd::ReadLine(char *To
, unsigned long long const Size
)
1096 return gzgets(d
->gz
, To
, Size
);
1099 unsigned long long read
= 0;
1100 if (Read(To
, Size
, &read
) == false)
1103 for (; *c
!= '\n' && *c
!= '\0' && read
!= 0; --read
, ++c
)
1104 ; // find the end of the line
1108 Seek(Tell() - read
);
1112 // FileFd::Write - Write to the file /*{{{*/
1113 // ---------------------------------------------------------------------
1115 bool FileFd::Write(const void *From
,unsigned long long Size
)
1123 Res
= gzwrite(d
->gz
,From
,Size
);
1126 Res
= write(iFd
,From
,Size
);
1127 if (Res
< 0 && errno
== EINTR
)
1132 return _error
->Errno("write",_("Write error"));
1135 From
= (char *)From
+ Res
;
1138 while (Res
> 0 && Size
> 0);
1144 return _error
->Error(_("write, still have %llu to write but couldn't"), Size
);
1147 // FileFd::Seek - Seek in the file /*{{{*/
1148 // ---------------------------------------------------------------------
1150 bool FileFd::Seek(unsigned long long To
)
1152 if (d
->pipe
== true)
1154 if ((d
->openmode
& ReadOnly
) != ReadOnly
)
1155 return _error
->Error("Reopen is only implemented for read-only files!");
1157 if (TemporaryFileName
.empty() == false)
1158 iFd
= open(TemporaryFileName
.c_str(), O_RDONLY
);
1159 else if (FileName
.empty() == false)
1160 iFd
= open(FileName
.c_str(), O_RDONLY
);
1162 return _error
->Error("Reopen is not implemented for OpenDescriptor()-FileFd!");
1164 if (OpenInternDescriptor(d
->openmode
, d
->compressor
) == false)
1165 return _error
->Error("Seek on file %s because it couldn't be reopened", FileName
.c_str());
1174 res
= gzseek(d
->gz
,To
,SEEK_SET
);
1177 res
= lseek(iFd
,To
,SEEK_SET
);
1178 if (res
!= (signed)To
)
1181 return _error
->Error("Unable to seek to %llu", To
);
1187 // FileFd::Skip - Seek in the file /*{{{*/
1188 // ---------------------------------------------------------------------
1190 bool FileFd::Skip(unsigned long long Over
)
1195 res
= gzseek(d
->gz
,Over
,SEEK_CUR
);
1198 res
= lseek(iFd
,Over
,SEEK_CUR
);
1202 return _error
->Error("Unable to seek ahead %llu",Over
);
1208 // FileFd::Truncate - Truncate the file /*{{{*/
1209 // ---------------------------------------------------------------------
1211 bool FileFd::Truncate(unsigned long long To
)
1216 return _error
->Error("Truncating gzipped files is not implemented (%s)", FileName
.c_str());
1218 if (ftruncate(iFd
,To
) != 0)
1221 return _error
->Error("Unable to truncate to %llu",To
);
1227 // FileFd::Tell - Current seek position /*{{{*/
1228 // ---------------------------------------------------------------------
1230 unsigned long long FileFd::Tell()
1235 Res
= gztell(d
->gz
);
1238 Res
= lseek(iFd
,0,SEEK_CUR
);
1239 if (Res
== (off_t
)-1)
1240 _error
->Errno("lseek","Failed to determine the current file position");
1244 // FileFd::FileSize - Return the size of the file /*{{{*/
1245 // ---------------------------------------------------------------------
1247 unsigned long long FileFd::FileSize()
1250 if (d
->pipe
== false && fstat(iFd
,&Buf
) != 0)
1251 return _error
->Errno("fstat","Unable to determine the file size");
1253 // for compressor pipes st_size is undefined and at 'best' zero
1254 if (d
->pipe
== true || S_ISFIFO(Buf
.st_mode
))
1256 // we set it here, too, as we get the info here for free
1257 // in theory the Open-methods should take care of it already
1259 if (stat(FileName
.c_str(), &Buf
) != 0)
1260 return _error
->Errno("stat","Unable to determine the file size");
1266 // FileFd::Size - Return the size of the content in the file /*{{{*/
1267 // ---------------------------------------------------------------------
1269 unsigned long long FileFd::Size()
1271 unsigned long long size
= FileSize();
1273 // for compressor pipes st_size is undefined and at 'best' zero,
1274 // so we 'read' the content and 'seek' back - see there
1275 if (d
->pipe
== true)
1277 // FIXME: If we have read first and then FileSize() the report is wrong
1280 unsigned long long read
= 0;
1282 Read(ignore
, sizeof(ignore
), &read
);
1288 // only check gzsize if we are actually a gzip file, just checking for
1289 // "gz" is not sufficient as uncompressed files could be opened with
1290 // gzopen in "direct" mode as well
1291 else if (d
->gz
&& !gzdirect(d
->gz
) && size
> 0)
1293 /* unfortunately zlib.h doesn't provide a gzsize(), so we have to do
1294 * this ourselves; the original (uncompressed) file size is the last 32
1295 * bits of the file */
1296 // FIXME: Size for gz-files is limited by 32bit… no largefile support
1297 off_t orig_pos
= lseek(iFd
, 0, SEEK_CUR
);
1298 if (lseek(iFd
, -4, SEEK_END
) < 0)
1299 return _error
->Errno("lseek","Unable to seek to end of gzipped file");
1301 if (read(iFd
, &size
, 4) != 4)
1302 return _error
->Errno("read","Unable to read original size of gzipped file");
1304 #ifdef WORDS_BIGENDIAN
1305 uint32_t tmp_size
= size
;
1306 uint8_t const * const p
= (uint8_t const * const) &tmp_size
;
1307 tmp_size
= (p
[3] << 24) | (p
[2] << 16) | (p
[1] << 8) | p
[0];
1311 if (lseek(iFd
, orig_pos
, SEEK_SET
) < 0)
1312 return _error
->Errno("lseek","Unable to seek in gzipped file");
1320 // FileFd::ModificationTime - Return the time of last touch /*{{{*/
1321 // ---------------------------------------------------------------------
1323 time_t FileFd::ModificationTime()
1326 if (d
->pipe
== false && fstat(iFd
,&Buf
) != 0)
1328 _error
->Errno("fstat","Unable to determine the modification time of file %s", FileName
.c_str());
1332 // for compressor pipes st_size is undefined and at 'best' zero
1333 if (d
->pipe
== true || S_ISFIFO(Buf
.st_mode
))
1335 // we set it here, too, as we get the info here for free
1336 // in theory the Open-methods should take care of it already
1338 if (stat(FileName
.c_str(), &Buf
) != 0)
1340 _error
->Errno("fstat","Unable to determine the modification time of file %s", FileName
.c_str());
1345 return Buf
.st_mtime
;
1348 // FileFd::Close - Close the file if the close flag is set /*{{{*/
1349 // ---------------------------------------------------------------------
1351 bool FileFd::Close()
1357 if ((Flags
& AutoClose
) == AutoClose
)
1360 if (d
!= NULL
&& d
->gz
!= NULL
) {
1361 int const e
= gzclose(d
->gz
);
1362 // gzdopen() on empty files always fails with "buffer error" here, ignore that
1363 if (e
!= 0 && e
!= Z_BUF_ERROR
)
1364 Res
&= _error
->Errno("close",_("Problem closing the gzip file %s"), FileName
.c_str());
1367 if (iFd
> 0 && close(iFd
) != 0)
1368 Res
&= _error
->Errno("close",_("Problem closing the file %s"), FileName
.c_str());
1371 if ((Flags
& Replace
) == Replace
&& iFd
>= 0) {
1372 if (rename(TemporaryFileName
.c_str(), FileName
.c_str()) != 0)
1373 Res
&= _error
->Errno("rename",_("Problem renaming the file %s to %s"), TemporaryFileName
.c_str(), FileName
.c_str());
1375 FileName
= TemporaryFileName
; // for the unlink() below.
1376 TemporaryFileName
.clear();
1381 if ((Flags
& Fail
) == Fail
&& (Flags
& DelOnFail
) == DelOnFail
&&
1382 FileName
.empty() == false)
1383 if (unlink(FileName
.c_str()) != 0)
1384 Res
&= _error
->WarningE("unlnk",_("Problem unlinking the file %s"), FileName
.c_str());
1388 if (d
->compressor_pid
> 0)
1389 ExecWait(d
->compressor_pid
, "FileFdCompressor", true);
1397 // FileFd::Sync - Sync the file /*{{{*/
1398 // ---------------------------------------------------------------------
1402 #ifdef _POSIX_SYNCHRONIZED_IO
1403 if (fsync(iFd
) != 0)
1404 return _error
->Errno("sync",_("Problem syncing the file"));
1410 gzFile
FileFd::gzFd() { return (gzFile
) d
->gz
; }