1 // -*- mode: cpp; mode: fold -*-
3 /* ######################################################################
7 CopyFile - Buffered copy of a single file
8 GetLock - dpkg compatible lock file manipulation (fcntl)
10 Most of this source is placed in the Public Domain, do with it what
12 It was originally written by Jason Gunthorpe <jgg@debian.org>.
13 FileFd gzip support added by Martin Pitt <martin.pitt@canonical.com>
15 The exception is RunScripts() it is under the GPLv2
17 ##################################################################### */
19 // Include Files /*{{{*/
22 #include <apt-pkg/fileutl.h>
23 #include <apt-pkg/strutl.h>
24 #include <apt-pkg/error.h>
25 #include <apt-pkg/sptr.h>
26 #include <apt-pkg/aptconfiguration.h>
27 #include <apt-pkg/configuration.h>
28 #include <apt-pkg/macros.h>
33 #include <sys/select.h>
65 #ifdef WORDS_BIGENDIAN
74 // RunScripts - Run a set of scripts from a configuration subtree /*{{{*/
75 // ---------------------------------------------------------------------
77 bool RunScripts(const char *Cnf
)
79 Configuration::Item
const *Opts
= _config
->Tree(Cnf
);
80 if (Opts
== 0 || Opts
->Child
== 0)
84 // Fork for running the system calls
85 pid_t Child
= ExecFork();
90 if (_config
->FindDir("DPkg::Chroot-Directory","/") != "/")
92 std::cerr
<< "Chrooting into "
93 << _config
->FindDir("DPkg::Chroot-Directory")
95 if (chroot(_config
->FindDir("DPkg::Chroot-Directory","/").c_str()) != 0)
99 if (chdir("/tmp/") != 0)
102 unsigned int Count
= 1;
103 for (; Opts
!= 0; Opts
= Opts
->Next
, Count
++)
105 if (Opts
->Value
.empty() == true)
108 if(_config
->FindB("Debug::RunScripts", false) == true)
109 std::clog
<< "Running external script: '"
110 << Opts
->Value
<< "'" << std::endl
;
112 if (system(Opts
->Value
.c_str()) != 0)
118 // Wait for the child
120 while (waitpid(Child
,&Status
,0) != Child
)
124 return _error
->Errno("waitpid","Couldn't wait for subprocess");
127 // Restore sig int/quit
128 signal(SIGQUIT
,SIG_DFL
);
129 signal(SIGINT
,SIG_DFL
);
131 // Check for an error code.
132 if (WIFEXITED(Status
) == 0 || WEXITSTATUS(Status
) != 0)
134 unsigned int Count
= WEXITSTATUS(Status
);
138 for (; Opts
!= 0 && Count
!= 1; Opts
= Opts
->Next
, Count
--);
139 _error
->Error("Problem executing scripts %s '%s'",Cnf
,Opts
->Value
.c_str());
142 return _error
->Error("Sub-process returned an error code");
149 // CopyFile - Buffered copy of a file /*{{{*/
150 // ---------------------------------------------------------------------
151 /* The caller is expected to set things so that failure causes erasure */
152 bool CopyFile(FileFd
&From
,FileFd
&To
)
154 if (From
.IsOpen() == false || To
.IsOpen() == false ||
155 From
.Failed() == true || To
.Failed() == true)
158 // Buffered copy between fds
159 SPtrArray
<unsigned char> Buf
= new unsigned char[64000];
160 unsigned long long Size
= From
.Size();
163 unsigned long long ToRead
= Size
;
167 if (From
.Read(Buf
,ToRead
) == false ||
168 To
.Write(Buf
,ToRead
) == false)
177 // GetLock - Gets a lock file /*{{{*/
178 // ---------------------------------------------------------------------
179 /* This will create an empty file of the given name and lock it. Once this
180 is done all other calls to GetLock in any other process will fail with
181 -1. The return result is the fd of the file, the call should call
182 close at some time. */
183 int GetLock(string File
,bool Errors
)
185 // GetLock() is used in aptitude on directories with public-write access
186 // Use O_NOFOLLOW here to prevent symlink traversal attacks
187 int FD
= open(File
.c_str(),O_RDWR
| O_CREAT
| O_NOFOLLOW
,0640);
190 // Read only .. can't have locking problems there.
193 _error
->Warning(_("Not using locking for read only lock file %s"),File
.c_str());
194 return dup(0); // Need something for the caller to close
198 _error
->Errno("open",_("Could not open lock file %s"),File
.c_str());
200 // Feh.. We do this to distinguish the lock vs open case..
204 SetCloseExec(FD
,true);
206 // Acquire a write lock
209 fl
.l_whence
= SEEK_SET
;
212 if (fcntl(FD
,F_SETLK
,&fl
) == -1)
214 // always close to not leak resources
221 _error
->Warning(_("Not using locking for nfs mounted lock file %s"),File
.c_str());
222 return dup(0); // Need something for the caller to close
226 _error
->Errno("open",_("Could not get lock %s"),File
.c_str());
234 // FileExists - Check if a file exists /*{{{*/
235 // ---------------------------------------------------------------------
236 /* Beware: Directories are also files! */
237 bool FileExists(string File
)
240 if (stat(File
.c_str(),&Buf
) != 0)
245 // RealFileExists - Check if a file exists and if it is really a file /*{{{*/
246 // ---------------------------------------------------------------------
248 bool RealFileExists(string File
)
251 if (stat(File
.c_str(),&Buf
) != 0)
253 return ((Buf
.st_mode
& S_IFREG
) != 0);
256 // DirectoryExists - Check if a directory exists and is really one /*{{{*/
257 // ---------------------------------------------------------------------
259 bool DirectoryExists(string
const &Path
)
262 if (stat(Path
.c_str(),&Buf
) != 0)
264 return ((Buf
.st_mode
& S_IFDIR
) != 0);
267 // CreateDirectory - poor man's mkdir -p guarded by a parent directory /*{{{*/
268 // ---------------------------------------------------------------------
269 /* This method will create all directories needed for path in good old
270 mkdir -p style but refuses to do this if Parent is not a prefix of
271 this Path. Example: /var/cache/ and /var/cache/apt/archives are given,
272 so it will create apt/archives if /var/cache exists - on the other
273 hand if the parent is /var/lib the creation will fail as this path
274 is not a parent of the path to be generated. */
275 bool CreateDirectory(string
const &Parent
, string
const &Path
)
277 if (Parent
.empty() == true || Path
.empty() == true)
280 if (DirectoryExists(Path
) == true)
283 if (DirectoryExists(Parent
) == false)
286 // we are not going to create directories "into the blue"
287 if (Path
.compare(0, Parent
.length(), Parent
) != 0)
290 vector
<string
> const dirs
= VectorizeString(Path
.substr(Parent
.size()), '/');
291 string progress
= Parent
;
292 for (vector
<string
>::const_iterator d
= dirs
.begin(); d
!= dirs
.end(); ++d
)
294 if (d
->empty() == true)
297 progress
.append("/").append(*d
);
298 if (DirectoryExists(progress
) == true)
301 if (mkdir(progress
.c_str(), 0755) != 0)
307 // CreateAPTDirectoryIfNeeded - ensure that the given directory exists /*{{{*/
308 // ---------------------------------------------------------------------
309 /* a small wrapper around CreateDirectory to check if it exists and to
310 remove the trailing "/apt/" from the parent directory if needed */
311 bool CreateAPTDirectoryIfNeeded(string
const &Parent
, string
const &Path
)
313 if (DirectoryExists(Path
) == true)
316 size_t const len
= Parent
.size();
317 if (len
> 5 && Parent
.find("/apt/", len
- 6, 5) == len
- 5)
319 if (CreateDirectory(Parent
.substr(0,len
-5), Path
) == true)
322 else if (CreateDirectory(Parent
, Path
) == true)
328 // GetListOfFilesInDir - returns a vector of files in the given dir /*{{{*/
329 // ---------------------------------------------------------------------
330 /* If an extension is given only files with this extension are included
331 in the returned vector, otherwise every "normal" file is included. */
332 std::vector
<string
> GetListOfFilesInDir(string
const &Dir
, string
const &Ext
,
333 bool const &SortList
, bool const &AllowNoExt
)
335 std::vector
<string
> ext
;
337 if (Ext
.empty() == false)
339 if (AllowNoExt
== true && ext
.empty() == false)
341 return GetListOfFilesInDir(Dir
, ext
, SortList
);
343 std::vector
<string
> GetListOfFilesInDir(string
const &Dir
, std::vector
<string
> const &Ext
,
344 bool const &SortList
)
346 // Attention debuggers: need to be set with the environment config file!
347 bool const Debug
= _config
->FindB("Debug::GetListOfFilesInDir", false);
350 std::clog
<< "Accept in " << Dir
<< " only files with the following " << Ext
.size() << " extensions:" << std::endl
;
351 if (Ext
.empty() == true)
352 std::clog
<< "\tNO extension" << std::endl
;
354 for (std::vector
<string
>::const_iterator e
= Ext
.begin();
356 std::clog
<< '\t' << (e
->empty() == true ? "NO" : *e
) << " extension" << std::endl
;
359 std::vector
<string
> List
;
361 if (DirectoryExists(Dir
) == false)
363 _error
->Error(_("List of files can't be created as '%s' is not a directory"), Dir
.c_str());
367 Configuration::MatchAgainstConfig
SilentIgnore("Dir::Ignore-Files-Silently");
368 DIR *D
= opendir(Dir
.c_str());
371 _error
->Errno("opendir",_("Unable to read %s"),Dir
.c_str());
375 for (struct dirent
*Ent
= readdir(D
); Ent
!= 0; Ent
= readdir(D
))
377 // skip "hidden" files
378 if (Ent
->d_name
[0] == '.')
381 // Make sure it is a file and not something else
382 string
const File
= flCombine(Dir
,Ent
->d_name
);
383 #ifdef _DIRENT_HAVE_D_TYPE
384 if (Ent
->d_type
!= DT_REG
)
387 if (RealFileExists(File
) == false)
389 // do not show ignoration warnings for directories
391 #ifdef _DIRENT_HAVE_D_TYPE
392 Ent
->d_type
== DT_DIR
||
394 DirectoryExists(File
) == true)
396 if (SilentIgnore
.Match(Ent
->d_name
) == false)
397 _error
->Notice(_("Ignoring '%s' in directory '%s' as it is not a regular file"), Ent
->d_name
, Dir
.c_str());
402 // check for accepted extension:
403 // no extension given -> periods are bad as hell!
404 // extensions given -> "" extension allows no extension
405 if (Ext
.empty() == false)
407 string d_ext
= flExtension(Ent
->d_name
);
408 if (d_ext
== Ent
->d_name
) // no extension
410 if (std::find(Ext
.begin(), Ext
.end(), "") == Ext
.end())
413 std::clog
<< "Bad file: " << Ent
->d_name
<< " → no extension" << std::endl
;
414 if (SilentIgnore
.Match(Ent
->d_name
) == false)
415 _error
->Notice(_("Ignoring file '%s' in directory '%s' as it has no filename extension"), Ent
->d_name
, Dir
.c_str());
419 else if (std::find(Ext
.begin(), Ext
.end(), d_ext
) == Ext
.end())
422 std::clog
<< "Bad file: " << Ent
->d_name
<< " → bad extension »" << flExtension(Ent
->d_name
) << "«" << std::endl
;
423 if (SilentIgnore
.Match(Ent
->d_name
) == false)
424 _error
->Notice(_("Ignoring file '%s' in directory '%s' as it has an invalid filename extension"), Ent
->d_name
, Dir
.c_str());
429 // Skip bad filenames ala run-parts
430 const char *C
= Ent
->d_name
;
432 if (isalpha(*C
) == 0 && isdigit(*C
) == 0
433 && *C
!= '_' && *C
!= '-' && *C
!= ':') {
434 // no required extension -> dot is a bad character
435 if (*C
== '.' && Ext
.empty() == false)
440 // we don't reach the end of the name -> bad character included
444 std::clog
<< "Bad file: " << Ent
->d_name
<< " → bad character »"
445 << *C
<< "« in filename (period allowed: " << (Ext
.empty() ? "no" : "yes") << ")" << std::endl
;
449 // skip filenames which end with a period. These are never valid
453 std::clog
<< "Bad file: " << Ent
->d_name
<< " → Period as last character" << std::endl
;
458 std::clog
<< "Accept file: " << Ent
->d_name
<< " in " << Dir
<< std::endl
;
459 List
.push_back(File
);
463 if (SortList
== true)
464 std::sort(List
.begin(),List
.end());
467 std::vector
<string
> GetListOfFilesInDir(string
const &Dir
, bool SortList
)
469 bool const Debug
= _config
->FindB("Debug::GetListOfFilesInDir", false);
471 std::clog
<< "Accept in " << Dir
<< " all regular files" << std::endl
;
473 std::vector
<string
> List
;
475 if (DirectoryExists(Dir
) == false)
477 _error
->Error(_("List of files can't be created as '%s' is not a directory"), Dir
.c_str());
481 DIR *D
= opendir(Dir
.c_str());
484 _error
->Errno("opendir",_("Unable to read %s"),Dir
.c_str());
488 for (struct dirent
*Ent
= readdir(D
); Ent
!= 0; Ent
= readdir(D
))
490 // skip "hidden" files
491 if (Ent
->d_name
[0] == '.')
494 // Make sure it is a file and not something else
495 string
const File
= flCombine(Dir
,Ent
->d_name
);
496 #ifdef _DIRENT_HAVE_D_TYPE
497 if (Ent
->d_type
!= DT_REG
)
500 if (RealFileExists(File
) == false)
503 std::clog
<< "Bad file: " << Ent
->d_name
<< " → it is not a real file" << std::endl
;
508 // Skip bad filenames ala run-parts
509 const char *C
= Ent
->d_name
;
511 if (isalpha(*C
) == 0 && isdigit(*C
) == 0
512 && *C
!= '_' && *C
!= '-' && *C
!= '.')
515 // we don't reach the end of the name -> bad character included
519 std::clog
<< "Bad file: " << Ent
->d_name
<< " → bad character »" << *C
<< "« in filename" << std::endl
;
523 // skip filenames which end with a period. These are never valid
527 std::clog
<< "Bad file: " << Ent
->d_name
<< " → Period as last character" << std::endl
;
532 std::clog
<< "Accept file: " << Ent
->d_name
<< " in " << Dir
<< std::endl
;
533 List
.push_back(File
);
537 if (SortList
== true)
538 std::sort(List
.begin(),List
.end());
542 // SafeGetCWD - This is a safer getcwd that returns a dynamic string /*{{{*/
543 // ---------------------------------------------------------------------
544 /* We return / on failure. */
547 // Stash the current dir.
550 if (getcwd(S
,sizeof(S
)-2) == 0)
552 unsigned int Len
= strlen(S
);
558 // GetModificationTime - Get the mtime of the given file or -1 on error /*{{{*/
559 // ---------------------------------------------------------------------
560 /* We return / on failure. */
561 time_t GetModificationTime(string
const &Path
)
564 if (stat(Path
.c_str(), &St
) < 0)
569 // flNotDir - Strip the directory from the filename /*{{{*/
570 // ---------------------------------------------------------------------
572 string
flNotDir(string File
)
574 string::size_type Res
= File
.rfind('/');
575 if (Res
== string::npos
)
578 return string(File
,Res
,Res
- File
.length());
581 // flNotFile - Strip the file from the directory name /*{{{*/
582 // ---------------------------------------------------------------------
583 /* Result ends in a / */
584 string
flNotFile(string File
)
586 string::size_type Res
= File
.rfind('/');
587 if (Res
== string::npos
)
590 return string(File
,0,Res
);
593 // flExtension - Return the extension for the file /*{{{*/
594 // ---------------------------------------------------------------------
596 string
flExtension(string File
)
598 string::size_type Res
= File
.rfind('.');
599 if (Res
== string::npos
)
602 return string(File
,Res
,Res
- File
.length());
605 // flNoLink - If file is a symlink then deref it /*{{{*/
606 // ---------------------------------------------------------------------
607 /* If the name is not a link then the returned path is the input. */
608 string
flNoLink(string File
)
611 if (lstat(File
.c_str(),&St
) != 0 || S_ISLNK(St
.st_mode
) == 0)
613 if (stat(File
.c_str(),&St
) != 0)
616 /* Loop resolving the link. There is no need to limit the number of
617 loops because the stat call above ensures that the symlink is not
625 if ((Res
= readlink(NFile
.c_str(),Buffer
,sizeof(Buffer
))) <= 0 ||
626 (size_t)Res
>= sizeof(Buffer
))
629 // Append or replace the previous path
631 if (Buffer
[0] == '/')
634 NFile
= flNotFile(NFile
) + Buffer
;
636 // See if we are done
637 if (lstat(NFile
.c_str(),&St
) != 0)
639 if (S_ISLNK(St
.st_mode
) == 0)
644 // flCombine - Combine a file and a directory /*{{{*/
645 // ---------------------------------------------------------------------
646 /* If the file is an absolute path then it is just returned, otherwise
647 the directory is pre-pended to it. */
648 string
flCombine(string Dir
,string File
)
650 if (File
.empty() == true)
653 if (File
[0] == '/' || Dir
.empty() == true)
655 if (File
.length() >= 2 && File
[0] == '.' && File
[1] == '/')
657 if (Dir
[Dir
.length()-1] == '/')
659 return Dir
+ '/' + File
;
662 // flAbsPath - Return the absolute path of the filename /*{{{*/
663 // ---------------------------------------------------------------------
665 string
flAbsPath(string File
)
667 char *p
= realpath(File
.c_str(), NULL
);
670 _error
->Errno("realpath", "flAbsPath failed");
673 std::string
AbsPath(p
);
678 // SetCloseExec - Set the close on exec flag /*{{{*/
679 // ---------------------------------------------------------------------
681 void SetCloseExec(int Fd
,bool Close
)
683 if (fcntl(Fd
,F_SETFD
,(Close
== false)?0:FD_CLOEXEC
) != 0)
685 cerr
<< "FATAL -> Could not set close on exec " << strerror(errno
) << endl
;
690 // SetNonBlock - Set the nonblocking flag /*{{{*/
691 // ---------------------------------------------------------------------
693 void SetNonBlock(int Fd
,bool Block
)
695 int Flags
= fcntl(Fd
,F_GETFL
) & (~O_NONBLOCK
);
696 if (fcntl(Fd
,F_SETFL
,Flags
| ((Block
== false)?0:O_NONBLOCK
)) != 0)
698 cerr
<< "FATAL -> Could not set non-blocking flag " << strerror(errno
) << endl
;
703 // WaitFd - Wait for a FD to become readable /*{{{*/
704 // ---------------------------------------------------------------------
705 /* This waits for a FD to become readable using select. It is useful for
706 applications making use of non-blocking sockets. The timeout is
708 bool WaitFd(int Fd
,bool write
,unsigned long timeout
)
721 Res
= select(Fd
+1,0,&Set
,0,(timeout
!= 0?&tv
:0));
723 while (Res
< 0 && errno
== EINTR
);
733 Res
= select(Fd
+1,&Set
,0,0,(timeout
!= 0?&tv
:0));
735 while (Res
< 0 && errno
== EINTR
);
744 // MergeKeepFdsFromConfiguration - Merge APT::Keep-Fds configuration /*{{{*/
745 // ---------------------------------------------------------------------
746 /* This is used to merge the APT::Keep-Fds with the provided KeepFDs
749 void MergeKeepFdsFromConfiguration(std::set
<int> &KeepFDs
)
751 Configuration::Item
const *Opts
= _config
->Tree("APT::Keep-Fds");
752 if (Opts
!= 0 && Opts
->Child
!= 0)
755 for (; Opts
!= 0; Opts
= Opts
->Next
)
757 if (Opts
->Value
.empty() == true)
759 int fd
= atoi(Opts
->Value
.c_str());
765 // ExecFork - Magical fork that sanitizes the context before execing /*{{{*/
766 // ---------------------------------------------------------------------
767 /* This is used if you want to cleanse the environment for the forked
768 child, it fixes up the important signals and nukes all of the fds,
769 otherwise acts like normal fork. */
773 // we need to merge the Keep-Fds as external tools like
774 // debconf-apt-progress use it
775 MergeKeepFdsFromConfiguration(KeepFDs
);
776 return ExecFork(KeepFDs
);
779 pid_t
ExecFork(std::set
<int> KeepFDs
)
781 // Fork off the process
782 pid_t Process
= fork();
785 cerr
<< "FATAL -> Failed to fork." << endl
;
789 // Spawn the subprocess
793 signal(SIGPIPE
,SIG_DFL
);
794 signal(SIGQUIT
,SIG_DFL
);
795 signal(SIGINT
,SIG_DFL
);
796 signal(SIGWINCH
,SIG_DFL
);
797 signal(SIGCONT
,SIG_DFL
);
798 signal(SIGTSTP
,SIG_DFL
);
800 // Close all of our FDs - just in case
801 for (int K
= 3; K
!= sysconf(_SC_OPEN_MAX
); K
++)
803 if(KeepFDs
.find(K
) == KeepFDs
.end())
804 fcntl(K
,F_SETFD
,FD_CLOEXEC
);
811 // ExecWait - Fancy waitpid /*{{{*/
812 // ---------------------------------------------------------------------
813 /* Waits for the given sub process. If Reap is set then no errors are
814 generated. Otherwise a failed subprocess will generate a proper descriptive
816 bool ExecWait(pid_t Pid
,const char *Name
,bool Reap
)
821 // Wait and collect the error code
823 while (waitpid(Pid
,&Status
,0) != Pid
)
831 return _error
->Error(_("Waited for %s but it wasn't there"),Name
);
835 // Check for an error code.
836 if (WIFEXITED(Status
) == 0 || WEXITSTATUS(Status
) != 0)
840 if (WIFSIGNALED(Status
) != 0)
842 if( WTERMSIG(Status
) == SIGSEGV
)
843 return _error
->Error(_("Sub-process %s received a segmentation fault."),Name
);
845 return _error
->Error(_("Sub-process %s received signal %u."),Name
, WTERMSIG(Status
));
848 if (WIFEXITED(Status
) != 0)
849 return _error
->Error(_("Sub-process %s returned an error code (%u)"),Name
,WEXITSTATUS(Status
));
851 return _error
->Error(_("Sub-process %s exited unexpectedly"),Name
);
858 class FileFdPrivate
{ /*{{{*/
869 uint8_t buffer
[4096];
875 LZMAFILE() : file(NULL
), eof(false), compressing(false) {}
877 if (compressing
== true)
880 stream
.avail_out
= sizeof(buffer
)/sizeof(buffer
[0]);
881 stream
.next_out
= buffer
;
882 err
= lzma_code(&stream
, LZMA_FINISH
);
883 if (err
!= LZMA_OK
&& err
!= LZMA_STREAM_END
)
885 _error
->Error("~LZMAFILE: Compress finalisation failed");
888 size_t const n
= sizeof(buffer
)/sizeof(buffer
[0]) - stream
.avail_out
;
889 if (n
&& fwrite(buffer
, 1, n
, file
) != n
)
891 _error
->Errno("~LZMAFILE",_("Write error"));
894 if (err
== LZMA_STREAM_END
)
905 pid_t compressor_pid
;
907 APT::Configuration::Compressor compressor
;
908 unsigned int openmode
;
909 unsigned long long seekpos
;
920 compressed_fd(-1), compressor_pid(-1), pipe(false),
921 openmode(0), seekpos(0) {};
922 bool InternalClose(std::string
const &FileName
)
925 /* dummy so that the rest can be 'else if's */;
927 else if (gz
!= NULL
) {
928 int const e
= gzclose(gz
);
930 // gzdclose() on empty files always fails with "buffer error" here, ignore that
931 if (e
!= 0 && e
!= Z_BUF_ERROR
)
932 return _error
->Errno("close",_("Problem closing the gzip file %s"), FileName
.c_str());
936 else if (bz2
!= NULL
) {
942 else if (lzma
!= NULL
) {
949 bool CloseDown(std::string
const &FileName
)
951 bool const Res
= InternalClose(FileName
);
953 if (compressor_pid
> 0)
954 ExecWait(compressor_pid
, "FileFdCompressor", true);
959 bool InternalStream() const {
971 ~FileFdPrivate() { CloseDown(""); }
974 // FileFd::Open - Open a file /*{{{*/
975 // ---------------------------------------------------------------------
976 /* The most commonly used open mode combinations are given with Mode */
977 bool FileFd::Open(string FileName
,unsigned int const Mode
,CompressMode Compress
, unsigned long const AccessMode
)
979 if (Mode
== ReadOnlyGzip
)
980 return Open(FileName
, ReadOnly
, Gzip
, AccessMode
);
982 if (Compress
== Auto
&& (Mode
& WriteOnly
) == WriteOnly
)
983 return FileFdError("Autodetection on %s only works in ReadOnly openmode!", FileName
.c_str());
985 std::vector
<APT::Configuration::Compressor
> const compressors
= APT::Configuration::getCompressors();
986 std::vector
<APT::Configuration::Compressor
>::const_iterator compressor
= compressors
.begin();
987 if (Compress
== Auto
)
989 for (; compressor
!= compressors
.end(); ++compressor
)
991 std::string file
= FileName
+ compressor
->Extension
;
992 if (FileExists(file
) == false)
998 else if (Compress
== Extension
)
1000 std::string::size_type
const found
= FileName
.find_last_of('.');
1002 if (found
!= std::string::npos
)
1004 ext
= FileName
.substr(found
);
1005 if (ext
== ".new" || ext
== ".bak")
1007 std::string::size_type
const found2
= FileName
.find_last_of('.', found
- 1);
1008 if (found2
!= std::string::npos
)
1009 ext
= FileName
.substr(found2
, found
- found2
);
1014 for (; compressor
!= compressors
.end(); ++compressor
)
1015 if (ext
== compressor
->Extension
)
1017 // no matching extension - assume uncompressed (imagine files like 'example.org_Packages')
1018 if (compressor
== compressors
.end())
1019 for (compressor
= compressors
.begin(); compressor
!= compressors
.end(); ++compressor
)
1020 if (compressor
->Name
== ".")
1028 case None
: name
= "."; break;
1029 case Gzip
: name
= "gzip"; break;
1030 case Bzip2
: name
= "bzip2"; break;
1031 case Lzma
: name
= "lzma"; break;
1032 case Xz
: name
= "xz"; break;
1036 return FileFdError("Opening File %s in None, Auto or Extension should be already handled?!?", FileName
.c_str());
1038 for (; compressor
!= compressors
.end(); ++compressor
)
1039 if (compressor
->Name
== name
)
1041 if (compressor
== compressors
.end())
1042 return FileFdError("Can't find a configured compressor %s for file %s", name
.c_str(), FileName
.c_str());
1045 if (compressor
== compressors
.end())
1046 return FileFdError("Can't find a match for specified compressor mode for file %s", FileName
.c_str());
1047 return Open(FileName
, Mode
, *compressor
, AccessMode
);
1049 bool FileFd::Open(string FileName
,unsigned int const Mode
,APT::Configuration::Compressor
const &compressor
, unsigned long const AccessMode
)
1054 if ((Mode
& WriteOnly
) != WriteOnly
&& (Mode
& (Atomic
| Create
| Empty
| Exclusive
)) != 0)
1055 return FileFdError("ReadOnly mode for %s doesn't accept additional flags!", FileName
.c_str());
1056 if ((Mode
& ReadWrite
) == 0)
1057 return FileFdError("No openmode provided in FileFd::Open for %s", FileName
.c_str());
1059 if ((Mode
& Atomic
) == Atomic
)
1063 else if ((Mode
& (Exclusive
| Create
)) == (Exclusive
| Create
))
1065 // for atomic, this will be done by rename in Close()
1066 unlink(FileName
.c_str());
1068 if ((Mode
& Empty
) == Empty
)
1071 if (lstat(FileName
.c_str(),&Buf
) == 0 && S_ISLNK(Buf
.st_mode
))
1072 unlink(FileName
.c_str());
1076 #define if_FLAGGED_SET(FLAG, MODE) if ((Mode & FLAG) == FLAG) fileflags |= MODE
1077 if_FLAGGED_SET(ReadWrite
, O_RDWR
);
1078 else if_FLAGGED_SET(ReadOnly
, O_RDONLY
);
1079 else if_FLAGGED_SET(WriteOnly
, O_WRONLY
);
1081 if_FLAGGED_SET(Create
, O_CREAT
);
1082 if_FLAGGED_SET(Empty
, O_TRUNC
);
1083 if_FLAGGED_SET(Exclusive
, O_EXCL
);
1084 #undef if_FLAGGED_SET
1086 if ((Mode
& Atomic
) == Atomic
)
1088 char *name
= strdup((FileName
+ ".XXXXXX").c_str());
1090 if((iFd
= mkstemp(name
)) == -1)
1093 return FileFdErrno("mkstemp", "Could not create temporary file for %s", FileName
.c_str());
1096 TemporaryFileName
= string(name
);
1099 // umask() will always set the umask and return the previous value, so
1100 // we first set the umask and then reset it to the old value
1101 mode_t
const CurrentUmask
= umask(0);
1102 umask(CurrentUmask
);
1103 // calculate the actual file permissions (just like open/creat)
1104 mode_t
const FilePermissions
= (AccessMode
& ~CurrentUmask
);
1106 if(fchmod(iFd
, FilePermissions
) == -1)
1107 return FileFdErrno("fchmod", "Could not change permissions for temporary file %s", TemporaryFileName
.c_str());
1110 iFd
= open(FileName
.c_str(), fileflags
, AccessMode
);
1112 this->FileName
= FileName
;
1113 if (iFd
== -1 || OpenInternDescriptor(Mode
, compressor
) == false)
1120 return FileFdErrno("open",_("Could not open file %s"), FileName
.c_str());
1123 SetCloseExec(iFd
,true);
1127 // FileFd::OpenDescriptor - Open a filedescriptor /*{{{*/
1128 // ---------------------------------------------------------------------
1130 bool FileFd::OpenDescriptor(int Fd
, unsigned int const Mode
, CompressMode Compress
, bool AutoClose
)
1132 std::vector
<APT::Configuration::Compressor
> const compressors
= APT::Configuration::getCompressors();
1133 std::vector
<APT::Configuration::Compressor
>::const_iterator compressor
= compressors
.begin();
1136 // compat with the old API
1137 if (Mode
== ReadOnlyGzip
&& Compress
== None
)
1142 case None
: name
= "."; break;
1143 case Gzip
: name
= "gzip"; break;
1144 case Bzip2
: name
= "bzip2"; break;
1145 case Lzma
: name
= "lzma"; break;
1146 case Xz
: name
= "xz"; break;
1149 if (AutoClose
== true && Fd
!= -1)
1151 return FileFdError("Opening Fd %d in Auto or Extension compression mode is not supported", Fd
);
1153 for (; compressor
!= compressors
.end(); ++compressor
)
1154 if (compressor
->Name
== name
)
1156 if (compressor
== compressors
.end())
1158 if (AutoClose
== true && Fd
!= -1)
1160 return FileFdError("Can't find a configured compressor %s for file %s", name
.c_str(), FileName
.c_str());
1162 return OpenDescriptor(Fd
, Mode
, *compressor
, AutoClose
);
1164 bool FileFd::OpenDescriptor(int Fd
, unsigned int const Mode
, APT::Configuration::Compressor
const &compressor
, bool AutoClose
)
1167 Flags
= (AutoClose
) ? FileFd::AutoClose
: 0;
1169 this->FileName
= "";
1170 if (OpenInternDescriptor(Mode
, compressor
) == false)
1173 (Flags
& Compressed
) == Compressed
||
1179 return FileFdError(_("Could not open file descriptor %d"), Fd
);
1183 bool FileFd::OpenInternDescriptor(unsigned int const Mode
, APT::Configuration::Compressor
const &compressor
)
1187 if (compressor
.Name
== "." || compressor
.Binary
.empty() == true)
1190 #if defined HAVE_ZLIB || defined HAVE_BZ2 || defined HAVE_LZMA
1191 // the API to open files is similar, so setup to avoid code duplicates later
1192 // and while at it ensure that we close before opening (if its a reopen)
1193 void* (*compress_open
)(int, const char *) = NULL
;
1195 /* dummy so that the rest can be 'else if's */;
1196 #define APT_COMPRESS_INIT(NAME,OPEN) \
1197 else if (compressor.Name == NAME) \
1199 compress_open = (void*(*)(int, const char *)) OPEN; \
1200 if (d != NULL) d->InternalClose(FileName); \
1203 APT_COMPRESS_INIT("gzip", gzdopen
)
1206 APT_COMPRESS_INIT("bzip2", BZ2_bzdopen
)
1209 APT_COMPRESS_INIT("xz", fdopen
)
1210 APT_COMPRESS_INIT("lzma", fdopen
)
1212 #undef APT_COMPRESS_INIT
1217 d
= new FileFdPrivate();
1219 d
->compressor
= compressor
;
1220 #if defined HAVE_ZLIB || defined HAVE_BZ2 || defined HAVE_LZMA
1221 if ((Flags
& AutoClose
) != AutoClose
&& compress_open
!= NULL
)
1223 // Need to duplicate fd here or gz/bz2 close for cleanup will close the fd as well
1224 int const internFd
= dup(iFd
);
1226 return FileFdErrno("OpenInternDescriptor", _("Could not open file descriptor %d"), iFd
);
1232 #if defined HAVE_ZLIB || defined HAVE_BZ2 || defined HAVE_LZMA
1233 if (compress_open
!= NULL
)
1235 void* compress_struct
= NULL
;
1236 if ((Mode
& ReadWrite
) == ReadWrite
)
1237 compress_struct
= compress_open(iFd
, "r+");
1238 else if ((Mode
& WriteOnly
) == WriteOnly
)
1239 compress_struct
= compress_open(iFd
, "w");
1241 compress_struct
= compress_open(iFd
, "r");
1242 if (compress_struct
== NULL
)
1246 /* dummy so that the rest can be 'else if's */;
1248 else if (compressor
.Name
== "gzip")
1249 d
->gz
= (gzFile
) compress_struct
;
1252 else if (compressor
.Name
== "bzip2")
1253 d
->bz2
= (BZFILE
*) compress_struct
;
1256 else if (compressor
.Name
== "xz" || compressor
.Name
== "lzma")
1258 uint32_t const xzlevel
= 6;
1259 uint64_t const memlimit
= UINT64_MAX
;
1260 if (d
->lzma
== NULL
)
1261 d
->lzma
= new FileFdPrivate::LZMAFILE
;
1262 d
->lzma
->file
= (FILE*) compress_struct
;
1263 d
->lzma
->stream
= LZMA_STREAM_INIT
;
1265 if ((Mode
& ReadWrite
) == ReadWrite
)
1266 return FileFdError("ReadWrite mode is not supported for file %s", FileName
.c_str());
1268 if ((Mode
& WriteOnly
) == WriteOnly
)
1270 if (compressor
.Name
== "xz")
1272 if (lzma_easy_encoder(&d
->lzma
->stream
, xzlevel
, LZMA_CHECK_CRC32
) != LZMA_OK
)
1277 lzma_options_lzma options
;
1278 lzma_lzma_preset(&options
, xzlevel
);
1279 if (lzma_alone_encoder(&d
->lzma
->stream
, &options
) != LZMA_OK
)
1282 d
->lzma
->compressing
= true;
1286 if (compressor
.Name
== "xz")
1288 if (lzma_auto_decoder(&d
->lzma
->stream
, memlimit
, 0) != LZMA_OK
)
1293 if (lzma_alone_decoder(&d
->lzma
->stream
, memlimit
) != LZMA_OK
)
1296 d
->lzma
->compressing
= false;
1300 Flags
|= Compressed
;
1305 // collect zombies here in case we reopen
1306 if (d
->compressor_pid
> 0)
1307 ExecWait(d
->compressor_pid
, "FileFdCompressor", true);
1309 if ((Mode
& ReadWrite
) == ReadWrite
)
1310 return FileFdError("ReadWrite mode is not supported for file %s", FileName
.c_str());
1312 bool const Comp
= (Mode
& WriteOnly
) == WriteOnly
;
1315 // Handle 'decompression' of empty files
1318 if (Buf
.st_size
== 0 && S_ISFIFO(Buf
.st_mode
) == false)
1321 // We don't need the file open - instead let the compressor open it
1322 // as he properly knows better how to efficiently read from 'his' file
1323 if (FileName
.empty() == false)
1330 // Create a data pipe
1331 int Pipe
[2] = {-1,-1};
1332 if (pipe(Pipe
) != 0)
1333 return FileFdErrno("pipe",_("Failed to create subprocess IPC"));
1334 for (int J
= 0; J
!= 2; J
++)
1335 SetCloseExec(Pipe
[J
],true);
1337 d
->compressed_fd
= iFd
;
1346 d
->compressor_pid
= ExecFork();
1347 if (d
->compressor_pid
== 0)
1351 dup2(d
->compressed_fd
,STDOUT_FILENO
);
1352 dup2(Pipe
[0],STDIN_FILENO
);
1356 if (d
->compressed_fd
!= -1)
1357 dup2(d
->compressed_fd
,STDIN_FILENO
);
1358 dup2(Pipe
[1],STDOUT_FILENO
);
1360 int const nullfd
= open("/dev/null", O_WRONLY
);
1363 dup2(nullfd
,STDERR_FILENO
);
1367 SetCloseExec(STDOUT_FILENO
,false);
1368 SetCloseExec(STDIN_FILENO
,false);
1370 std::vector
<char const*> Args
;
1371 Args
.push_back(compressor
.Binary
.c_str());
1372 std::vector
<std::string
> const * const addArgs
=
1373 (Comp
== true) ? &(compressor
.CompressArgs
) : &(compressor
.UncompressArgs
);
1374 for (std::vector
<std::string
>::const_iterator a
= addArgs
->begin();
1375 a
!= addArgs
->end(); ++a
)
1376 Args
.push_back(a
->c_str());
1377 if (Comp
== false && FileName
.empty() == false)
1379 // commands not needing arguments, do not need to be told about using standard output
1380 // in reality, only testcases with tools like cat, rev, rot13, … are able to trigger this
1381 if (compressor
.CompressArgs
.empty() == false && compressor
.UncompressArgs
.empty() == false)
1382 Args
.push_back("--stdout");
1383 if (TemporaryFileName
.empty() == false)
1384 Args
.push_back(TemporaryFileName
.c_str());
1386 Args
.push_back(FileName
.c_str());
1388 Args
.push_back(NULL
);
1390 execvp(Args
[0],(char **)&Args
[0]);
1391 cerr
<< _("Failed to exec compressor ") << Args
[0] << endl
;
1402 // FileFd::~File - Closes the file /*{{{*/
1403 // ---------------------------------------------------------------------
1404 /* If the proper modes are selected then we close the Fd and possibly
1405 unlink the file on error. */
1410 d
->CloseDown(FileName
);
1415 // FileFd::Read - Read a bit of the file /*{{{*/
1416 // ---------------------------------------------------------------------
1417 /* We are careful to handle interruption by a signal while reading
1419 bool FileFd::Read(void *To
,unsigned long long Size
,unsigned long long *Actual
)
1425 *((char *)To
) = '\0';
1429 /* dummy so that the rest can be 'else if's */;
1431 else if (d
!= NULL
&& d
->gz
!= NULL
)
1432 Res
= gzread(d
->gz
,To
,Size
);
1435 else if (d
!= NULL
&& d
->bz2
!= NULL
)
1436 Res
= BZ2_bzread(d
->bz2
,To
,Size
);
1439 else if (d
!= NULL
&& d
->lzma
!= NULL
)
1441 if (d
->lzma
->eof
== true)
1444 d
->lzma
->stream
.next_out
= (uint8_t *) To
;
1445 d
->lzma
->stream
.avail_out
= Size
;
1446 if (d
->lzma
->stream
.avail_in
== 0)
1448 d
->lzma
->stream
.next_in
= d
->lzma
->buffer
;
1449 d
->lzma
->stream
.avail_in
= fread(d
->lzma
->buffer
, 1, sizeof(d
->lzma
->buffer
)/sizeof(d
->lzma
->buffer
[0]), d
->lzma
->file
);
1451 d
->lzma
->err
= lzma_code(&d
->lzma
->stream
, LZMA_RUN
);
1452 if (d
->lzma
->err
== LZMA_STREAM_END
)
1454 d
->lzma
->eof
= true;
1455 Res
= Size
- d
->lzma
->stream
.avail_out
;
1457 else if (d
->lzma
->err
!= LZMA_OK
)
1464 Res
= Size
- d
->lzma
->stream
.avail_out
;
1467 // lzma run was okay, but produced no output…
1475 Res
= read(iFd
,To
,Size
);
1481 // trick the while-loop into running again
1487 /* dummy so that the rest can be 'else if's */;
1489 else if (d
!= NULL
&& d
->gz
!= NULL
)
1492 char const * const errmsg
= gzerror(d
->gz
, &err
);
1494 return FileFdError("gzread: %s (%d: %s)", _("Read error"), err
, errmsg
);
1498 else if (d
!= NULL
&& d
->bz2
!= NULL
)
1501 char const * const errmsg
= BZ2_bzerror(d
->bz2
, &err
);
1502 if (err
!= BZ_IO_ERROR
)
1503 return FileFdError("BZ2_bzread: %s (%d: %s)", _("Read error"), err
, errmsg
);
1507 else if (d
!= NULL
&& d
->lzma
!= NULL
)
1508 return FileFdError("lzma_read: %s (%d)", _("Read error"), d
->lzma
->err
);
1510 return FileFdErrno("read",_("Read error"));
1513 To
= (char *)To
+ Res
;
1520 while (Res
> 0 && Size
> 0);
1532 return FileFdError(_("read, still have %llu to read but none left"), Size
);
1535 // FileFd::ReadLine - Read a complete line from the file /*{{{*/
1536 // ---------------------------------------------------------------------
1537 /* Beware: This method can be quiet slow for big buffers on UNcompressed
1538 files because of the naive implementation! */
1539 char* FileFd::ReadLine(char *To
, unsigned long long const Size
)
1543 if (d
!= NULL
&& d
->gz
!= NULL
)
1544 return gzgets(d
->gz
, To
, Size
);
1547 unsigned long long read
= 0;
1548 while ((Size
- 1) != read
)
1550 unsigned long long done
= 0;
1551 if (Read(To
+ read
, 1, &done
) == false)
1555 if (To
[read
++] == '\n')
1564 // FileFd::Write - Write to the file /*{{{*/
1565 // ---------------------------------------------------------------------
1567 bool FileFd::Write(const void *From
,unsigned long long Size
)
1574 /* dummy so that the rest can be 'else if's */;
1576 else if (d
!= NULL
&& d
->gz
!= NULL
)
1577 Res
= gzwrite(d
->gz
,From
,Size
);
1580 else if (d
!= NULL
&& d
->bz2
!= NULL
)
1581 Res
= BZ2_bzwrite(d
->bz2
,(void*)From
,Size
);
1584 else if (d
!= NULL
&& d
->lzma
!= NULL
)
1586 d
->lzma
->stream
.next_in
= (uint8_t *)From
;
1587 d
->lzma
->stream
.avail_in
= Size
;
1588 d
->lzma
->stream
.next_out
= d
->lzma
->buffer
;
1589 d
->lzma
->stream
.avail_out
= sizeof(d
->lzma
->buffer
)/sizeof(d
->lzma
->buffer
[0]);
1590 d
->lzma
->err
= lzma_code(&d
->lzma
->stream
, LZMA_RUN
);
1591 if (d
->lzma
->err
!= LZMA_OK
)
1593 size_t const n
= sizeof(d
->lzma
->buffer
)/sizeof(d
->lzma
->buffer
[0]) - d
->lzma
->stream
.avail_out
;
1594 size_t const m
= (n
== 0) ? 0 : fwrite(d
->lzma
->buffer
, 1, n
, d
->lzma
->file
);
1598 Res
= Size
- d
->lzma
->stream
.avail_in
;
1602 Res
= write(iFd
,From
,Size
);
1604 if (Res
< 0 && errno
== EINTR
)
1609 /* dummy so that the rest can be 'else if's */;
1611 else if (d
!= NULL
&& d
->gz
!= NULL
)
1614 char const * const errmsg
= gzerror(d
->gz
, &err
);
1616 return FileFdError("gzwrite: %s (%d: %s)", _("Write error"), err
, errmsg
);
1620 else if (d
!= NULL
&& d
->bz2
!= NULL
)
1623 char const * const errmsg
= BZ2_bzerror(d
->bz2
, &err
);
1624 if (err
!= BZ_IO_ERROR
)
1625 return FileFdError("BZ2_bzwrite: %s (%d: %s)", _("Write error"), err
, errmsg
);
1629 else if (d
!= NULL
&& d
->lzma
!= NULL
)
1630 return FileFdErrno("lzma_fwrite", _("Write error"));
1632 return FileFdErrno("write",_("Write error"));
1635 From
= (char const *)From
+ Res
;
1640 while (Res
> 0 && Size
> 0);
1645 return FileFdError(_("write, still have %llu to write but couldn't"), Size
);
1647 bool FileFd::Write(int Fd
, const void *From
, unsigned long long Size
)
1653 Res
= write(Fd
,From
,Size
);
1654 if (Res
< 0 && errno
== EINTR
)
1657 return _error
->Errno("write",_("Write error"));
1659 From
= (char const *)From
+ Res
;
1662 while (Res
> 0 && Size
> 0);
1667 return _error
->Error(_("write, still have %llu to write but couldn't"), Size
);
1670 // FileFd::Seek - Seek in the file /*{{{*/
1671 // ---------------------------------------------------------------------
1673 bool FileFd::Seek(unsigned long long To
)
1677 if (d
!= NULL
&& (d
->pipe
== true || d
->InternalStream() == true))
1679 // Our poor man seeking in pipes is costly, so try to avoid it
1680 unsigned long long seekpos
= Tell();
1683 else if (seekpos
< To
)
1684 return Skip(To
- seekpos
);
1686 if ((d
->openmode
& ReadOnly
) != ReadOnly
)
1687 return FileFdError("Reopen is only implemented for read-only files!");
1688 d
->InternalClose(FileName
);
1692 if (TemporaryFileName
.empty() == false)
1693 iFd
= open(TemporaryFileName
.c_str(), O_RDONLY
);
1694 else if (FileName
.empty() == false)
1695 iFd
= open(FileName
.c_str(), O_RDONLY
);
1698 if (d
->compressed_fd
> 0)
1699 if (lseek(d
->compressed_fd
, 0, SEEK_SET
) != 0)
1700 iFd
= d
->compressed_fd
;
1702 return FileFdError("Reopen is not implemented for pipes opened with FileFd::OpenDescriptor()!");
1705 if (OpenInternDescriptor(d
->openmode
, d
->compressor
) == false)
1706 return FileFdError("Seek on file %s because it couldn't be reopened", FileName
.c_str());
1716 if (d
!= NULL
&& d
->gz
)
1717 res
= gzseek(d
->gz
,To
,SEEK_SET
);
1720 res
= lseek(iFd
,To
,SEEK_SET
);
1721 if (res
!= (off_t
)To
)
1722 return FileFdError("Unable to seek to %llu", To
);
1729 // FileFd::Skip - Seek in the file /*{{{*/
1730 // ---------------------------------------------------------------------
1732 bool FileFd::Skip(unsigned long long Over
)
1734 if (d
!= NULL
&& (d
->pipe
== true || d
->InternalStream() == true))
1739 unsigned long long toread
= std::min((unsigned long long) sizeof(buffer
), Over
);
1740 if (Read(buffer
, toread
) == false)
1741 return FileFdError("Unable to seek ahead %llu",Over
);
1749 if (d
!= NULL
&& d
->gz
!= NULL
)
1750 res
= gzseek(d
->gz
,Over
,SEEK_CUR
);
1753 res
= lseek(iFd
,Over
,SEEK_CUR
);
1755 return FileFdError("Unable to seek ahead %llu",Over
);
1762 // FileFd::Truncate - Truncate the file /*{{{*/
1763 // ---------------------------------------------------------------------
1765 bool FileFd::Truncate(unsigned long long To
)
1767 // truncating /dev/null is always successful - as we get an error otherwise
1768 if (To
== 0 && FileName
== "/dev/null")
1770 #if defined HAVE_ZLIB || defined HAVE_BZ2 || defined HAVE_LZMA
1771 if (d
!= NULL
&& (d
->InternalStream() == true
1776 return FileFdError("Truncating compressed files is not implemented (%s)", FileName
.c_str());
1778 if (ftruncate(iFd
,To
) != 0)
1779 return FileFdError("Unable to truncate to %llu",To
);
1784 // FileFd::Tell - Current seek position /*{{{*/
1785 // ---------------------------------------------------------------------
1787 unsigned long long FileFd::Tell()
1789 // In theory, we could just return seekpos here always instead of
1790 // seeking around, but not all users of FileFd use always Seek() and co
1791 // so d->seekpos isn't always true and we can just use it as a hint if
1792 // we have nothing else, but not always as an authority…
1793 if (d
!= NULL
&& (d
->pipe
== true || d
->InternalStream() == true))
1798 if (d
!= NULL
&& d
->gz
!= NULL
)
1799 Res
= gztell(d
->gz
);
1802 Res
= lseek(iFd
,0,SEEK_CUR
);
1803 if (Res
== (off_t
)-1)
1804 FileFdErrno("lseek","Failed to determine the current file position");
1810 static bool StatFileFd(char const * const msg
, int const iFd
, std::string
const &FileName
, struct stat
&Buf
, FileFdPrivate
* const d
) /*{{{*/
1812 bool ispipe
= (d
!= NULL
&& d
->pipe
== true);
1813 if (ispipe
== false)
1815 if (fstat(iFd
,&Buf
) != 0)
1816 // higher-level code will generate more meaningful messages,
1817 // even translated this would be meaningless for users
1818 return _error
->Errno("fstat", "Unable to determine %s for fd %i", msg
, iFd
);
1819 ispipe
= S_ISFIFO(Buf
.st_mode
);
1822 // for compressor pipes st_size is undefined and at 'best' zero
1825 // we set it here, too, as we get the info here for free
1826 // in theory the Open-methods should take care of it already
1829 if (stat(FileName
.c_str(), &Buf
) != 0)
1830 return _error
->Errno("fstat", "Unable to determine %s for file %s", msg
, FileName
.c_str());
1835 // FileFd::FileSize - Return the size of the file /*{{{*/
1836 unsigned long long FileFd::FileSize()
1839 if (StatFileFd("file size", iFd
, FileName
, Buf
, d
) == false)
1847 // FileFd::ModificationTime - Return the time of last touch /*{{{*/
1848 time_t FileFd::ModificationTime()
1851 if (StatFileFd("modification time", iFd
, FileName
, Buf
, d
) == false)
1856 return Buf
.st_mtime
;
1859 // FileFd::Size - Return the size of the content in the file /*{{{*/
1860 // ---------------------------------------------------------------------
1862 unsigned long long FileFd::Size()
1864 unsigned long long size
= FileSize();
1866 // for compressor pipes st_size is undefined and at 'best' zero,
1867 // so we 'read' the content and 'seek' back - see there
1868 if (d
!= NULL
&& (d
->pipe
== true || (d
->InternalStream() == true && size
> 0)))
1870 unsigned long long const oldSeek
= Tell();
1872 unsigned long long read
= 0;
1874 if (Read(ignore
, sizeof(ignore
), &read
) == false)
1884 // only check gzsize if we are actually a gzip file, just checking for
1885 // "gz" is not sufficient as uncompressed files could be opened with
1886 // gzopen in "direct" mode as well
1887 else if (d
!= NULL
&& d
->gz
&& !gzdirect(d
->gz
) && size
> 0)
1889 off_t
const oldPos
= lseek(iFd
,0,SEEK_CUR
);
1890 /* unfortunately zlib.h doesn't provide a gzsize(), so we have to do
1891 * this ourselves; the original (uncompressed) file size is the last 32
1892 * bits of the file */
1893 // FIXME: Size for gz-files is limited by 32bit… no largefile support
1894 if (lseek(iFd
, -4, SEEK_END
) < 0)
1896 FileFdErrno("lseek","Unable to seek to end of gzipped file");
1900 if (read(iFd
, &size
, 4) != 4)
1902 FileFdErrno("read","Unable to read original size of gzipped file");
1906 #ifdef WORDS_BIGENDIAN
1907 uint32_t tmp_size
= size
;
1908 uint8_t const * const p
= (uint8_t const * const) &tmp_size
;
1909 tmp_size
= (p
[3] << 24) | (p
[2] << 16) | (p
[1] << 8) | p
[0];
1913 if (lseek(iFd
, oldPos
, SEEK_SET
) < 0)
1915 FileFdErrno("lseek","Unable to seek in gzipped file");
1926 // FileFd::Close - Close the file if the close flag is set /*{{{*/
1927 // ---------------------------------------------------------------------
1929 bool FileFd::Close()
1935 if ((Flags
& AutoClose
) == AutoClose
)
1937 if ((Flags
& Compressed
) != Compressed
&& iFd
> 0 && close(iFd
) != 0)
1938 Res
&= _error
->Errno("close",_("Problem closing the file %s"), FileName
.c_str());
1941 Res
&= d
->CloseDown(FileName
);
1947 if ((Flags
& Replace
) == Replace
) {
1948 if (rename(TemporaryFileName
.c_str(), FileName
.c_str()) != 0)
1949 Res
&= _error
->Errno("rename",_("Problem renaming the file %s to %s"), TemporaryFileName
.c_str(), FileName
.c_str());
1951 FileName
= TemporaryFileName
; // for the unlink() below.
1952 TemporaryFileName
.clear();
1957 if ((Flags
& Fail
) == Fail
&& (Flags
& DelOnFail
) == DelOnFail
&&
1958 FileName
.empty() == false)
1959 if (unlink(FileName
.c_str()) != 0)
1960 Res
&= _error
->WarningE("unlnk",_("Problem unlinking the file %s"), FileName
.c_str());
1967 // FileFd::Sync - Sync the file /*{{{*/
1968 // ---------------------------------------------------------------------
1972 if (fsync(iFd
) != 0)
1973 return FileFdErrno("sync",_("Problem syncing the file"));
1977 // FileFd::FileFdErrno - set Fail and call _error->Errno *{{{*/
1978 bool FileFd::FileFdErrno(const char *Function
, const char *Description
,...)
1982 size_t msgSize
= 400;
1983 int const errsv
= errno
;
1986 va_start(args
,Description
);
1987 if (_error
->InsertErrno(GlobalError::ERROR
, Function
, Description
, args
, errsv
, msgSize
) == false)
1994 // FileFd::FileFdError - set Fail and call _error->Error *{{{*/
1995 bool FileFd::FileFdError(const char *Description
,...) {
1998 size_t msgSize
= 400;
2001 va_start(args
,Description
);
2002 if (_error
->Insert(GlobalError::ERROR
, Description
, args
, msgSize
) == false)
2010 APT_DEPRECATED gzFile
FileFd::gzFd() {
2019 // Glob - wrapper around "glob()" /*{{{*/
2020 // ---------------------------------------------------------------------
2022 std::vector
<std::string
> Glob(std::string
const &pattern
, int flags
)
2024 std::vector
<std::string
> result
;
2029 glob_res
= glob(pattern
.c_str(), flags
, NULL
, &globbuf
);
2033 if(glob_res
!= GLOB_NOMATCH
) {
2034 _error
->Errno("glob", "Problem with glob");
2040 for(i
=0;i
<globbuf
.gl_pathc
;i
++)
2041 result
.push_back(string(globbuf
.gl_pathv
[i
]));
2048 std::string
GetTempDir()
2050 const char *tmpdir
= getenv("TMPDIR");
2057 // check that tmpdir is set and exists
2059 if (!tmpdir
|| strlen(tmpdir
) == 0 || stat(tmpdir
, &st
) != 0)
2062 return string(tmpdir
);
2065 FileFd
* GetTempFile(std::string
const &Prefix
, bool ImmediateUnlink
)
2068 FileFd
*Fd
= new FileFd();
2070 std::string tempdir
= GetTempDir();
2071 snprintf(fn
, sizeof(fn
), "%s/%s.XXXXXX",
2072 tempdir
.c_str(), Prefix
.c_str());
2073 int fd
= mkstemp(fn
);
2078 _error
->Errno("GetTempFile",_("Unable to mkstemp %s"), fn
);
2081 if (!Fd
->OpenDescriptor(fd
, FileFd::WriteOnly
, FileFd::None
, true))
2083 _error
->Errno("GetTempFile",_("Unable to write to %s"),fn
);
2090 bool Rename(std::string From
, std::string To
)
2092 if (rename(From
.c_str(),To
.c_str()) != 0)
2094 _error
->Error(_("rename failed, %s (%s -> %s)."),strerror(errno
),
2095 From
.c_str(),To
.c_str());
2101 bool Popen(const char* Args
[], FileFd
&Fd
, pid_t
&Child
, FileFd::OpenMode Mode
)
2104 if (Mode
!= FileFd::ReadOnly
&& Mode
!= FileFd::WriteOnly
)
2105 return _error
->Error("Popen supports ReadOnly (x)or WriteOnly mode only");
2107 int Pipe
[2] = {-1, -1};
2110 return _error
->Errno("pipe", _("Failed to create subprocess IPC"));
2113 std::set
<int> keep_fds
;
2114 keep_fds
.insert(Pipe
[0]);
2115 keep_fds
.insert(Pipe
[1]);
2116 Child
= ExecFork(keep_fds
);
2118 return _error
->Errno("fork", "Failed to fork");
2121 if(Mode
== FileFd::ReadOnly
)
2126 else if(Mode
== FileFd::WriteOnly
)
2132 if(Mode
== FileFd::ReadOnly
)
2136 } else if(Mode
== FileFd::WriteOnly
)
2139 execv(Args
[0], (char**)Args
);
2142 if(Mode
== FileFd::ReadOnly
)
2146 } else if(Mode
== FileFd::WriteOnly
)
2151 Fd
.OpenDescriptor(fd
, Mode
, FileFd::None
, true);