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 // SetCloseExec - Set the close on exec flag /*{{{*/
663 // ---------------------------------------------------------------------
665 void SetCloseExec(int Fd
,bool Close
)
667 if (fcntl(Fd
,F_SETFD
,(Close
== false)?0:FD_CLOEXEC
) != 0)
669 cerr
<< "FATAL -> Could not set close on exec " << strerror(errno
) << endl
;
674 // SetNonBlock - Set the nonblocking flag /*{{{*/
675 // ---------------------------------------------------------------------
677 void SetNonBlock(int Fd
,bool Block
)
679 int Flags
= fcntl(Fd
,F_GETFL
) & (~O_NONBLOCK
);
680 if (fcntl(Fd
,F_SETFL
,Flags
| ((Block
== false)?0:O_NONBLOCK
)) != 0)
682 cerr
<< "FATAL -> Could not set non-blocking flag " << strerror(errno
) << endl
;
687 // WaitFd - Wait for a FD to become readable /*{{{*/
688 // ---------------------------------------------------------------------
689 /* This waits for a FD to become readable using select. It is useful for
690 applications making use of non-blocking sockets. The timeout is
692 bool WaitFd(int Fd
,bool write
,unsigned long timeout
)
705 Res
= select(Fd
+1,0,&Set
,0,(timeout
!= 0?&tv
:0));
707 while (Res
< 0 && errno
== EINTR
);
717 Res
= select(Fd
+1,&Set
,0,0,(timeout
!= 0?&tv
:0));
719 while (Res
< 0 && errno
== EINTR
);
728 // MergeKeepFdsFromConfiguration - Merge APT::Keep-Fds configuration /*{{{*/
729 // ---------------------------------------------------------------------
730 /* This is used to merge the APT::Keep-Fds with the provided KeepFDs
733 void MergeKeepFdsFromConfiguration(std::set
<int> &KeepFDs
)
735 Configuration::Item
const *Opts
= _config
->Tree("APT::Keep-Fds");
736 if (Opts
!= 0 && Opts
->Child
!= 0)
739 for (; Opts
!= 0; Opts
= Opts
->Next
)
741 if (Opts
->Value
.empty() == true)
743 int fd
= atoi(Opts
->Value
.c_str());
749 // ExecFork - Magical fork that sanitizes the context before execing /*{{{*/
750 // ---------------------------------------------------------------------
751 /* This is used if you want to cleanse the environment for the forked
752 child, it fixes up the important signals and nukes all of the fds,
753 otherwise acts like normal fork. */
757 // we need to merge the Keep-Fds as external tools like
758 // debconf-apt-progress use it
759 MergeKeepFdsFromConfiguration(KeepFDs
);
760 return ExecFork(KeepFDs
);
763 pid_t
ExecFork(std::set
<int> KeepFDs
)
765 // Fork off the process
766 pid_t Process
= fork();
769 cerr
<< "FATAL -> Failed to fork." << endl
;
773 // Spawn the subprocess
777 signal(SIGPIPE
,SIG_DFL
);
778 signal(SIGQUIT
,SIG_DFL
);
779 signal(SIGINT
,SIG_DFL
);
780 signal(SIGWINCH
,SIG_DFL
);
781 signal(SIGCONT
,SIG_DFL
);
782 signal(SIGTSTP
,SIG_DFL
);
784 // Close all of our FDs - just in case
785 for (int K
= 3; K
!= sysconf(_SC_OPEN_MAX
); K
++)
787 if(KeepFDs
.find(K
) == KeepFDs
.end())
788 fcntl(K
,F_SETFD
,FD_CLOEXEC
);
795 // ExecWait - Fancy waitpid /*{{{*/
796 // ---------------------------------------------------------------------
797 /* Waits for the given sub process. If Reap is set then no errors are
798 generated. Otherwise a failed subprocess will generate a proper descriptive
800 bool ExecWait(pid_t Pid
,const char *Name
,bool Reap
)
805 // Wait and collect the error code
807 while (waitpid(Pid
,&Status
,0) != Pid
)
815 return _error
->Error(_("Waited for %s but it wasn't there"),Name
);
819 // Check for an error code.
820 if (WIFEXITED(Status
) == 0 || WEXITSTATUS(Status
) != 0)
824 if (WIFSIGNALED(Status
) != 0)
826 if( WTERMSIG(Status
) == SIGSEGV
)
827 return _error
->Error(_("Sub-process %s received a segmentation fault."),Name
);
829 return _error
->Error(_("Sub-process %s received signal %u."),Name
, WTERMSIG(Status
));
832 if (WIFEXITED(Status
) != 0)
833 return _error
->Error(_("Sub-process %s returned an error code (%u)"),Name
,WEXITSTATUS(Status
));
835 return _error
->Error(_("Sub-process %s exited unexpectedly"),Name
);
842 class FileFdPrivate
{ /*{{{*/
853 uint8_t buffer
[4096];
859 LZMAFILE() : file(NULL
), eof(false), compressing(false) {}
861 if (compressing
== true)
864 stream
.avail_out
= sizeof(buffer
)/sizeof(buffer
[0]);
865 stream
.next_out
= buffer
;
866 err
= lzma_code(&stream
, LZMA_FINISH
);
867 if (err
!= LZMA_OK
&& err
!= LZMA_STREAM_END
)
869 _error
->Error("~LZMAFILE: Compress finalisation failed");
872 size_t const n
= sizeof(buffer
)/sizeof(buffer
[0]) - stream
.avail_out
;
873 if (n
&& fwrite(buffer
, 1, n
, file
) != n
)
875 _error
->Errno("~LZMAFILE",_("Write error"));
878 if (err
== LZMA_STREAM_END
)
889 pid_t compressor_pid
;
891 APT::Configuration::Compressor compressor
;
892 unsigned int openmode
;
893 unsigned long long seekpos
;
904 compressed_fd(-1), compressor_pid(-1), pipe(false),
905 openmode(0), seekpos(0) {};
906 bool InternalClose(std::string
const &FileName
)
909 /* dummy so that the rest can be 'else if's */;
911 else if (gz
!= NULL
) {
912 int const e
= gzclose(gz
);
914 // gzdclose() on empty files always fails with "buffer error" here, ignore that
915 if (e
!= 0 && e
!= Z_BUF_ERROR
)
916 return _error
->Errno("close",_("Problem closing the gzip file %s"), FileName
.c_str());
920 else if (bz2
!= NULL
) {
926 else if (lzma
!= NULL
) {
933 bool CloseDown(std::string
const &FileName
)
935 bool const Res
= InternalClose(FileName
);
937 if (compressor_pid
> 0)
938 ExecWait(compressor_pid
, "FileFdCompressor", true);
943 bool InternalStream() const {
955 ~FileFdPrivate() { CloseDown(""); }
958 // FileFd::Open - Open a file /*{{{*/
959 // ---------------------------------------------------------------------
960 /* The most commonly used open mode combinations are given with Mode */
961 bool FileFd::Open(string FileName
,unsigned int const Mode
,CompressMode Compress
, unsigned long const AccessMode
)
963 if (Mode
== ReadOnlyGzip
)
964 return Open(FileName
, ReadOnly
, Gzip
, AccessMode
);
966 if (Compress
== Auto
&& (Mode
& WriteOnly
) == WriteOnly
)
967 return FileFdError("Autodetection on %s only works in ReadOnly openmode!", FileName
.c_str());
969 std::vector
<APT::Configuration::Compressor
> const compressors
= APT::Configuration::getCompressors();
970 std::vector
<APT::Configuration::Compressor
>::const_iterator compressor
= compressors
.begin();
971 if (Compress
== Auto
)
973 for (; compressor
!= compressors
.end(); ++compressor
)
975 std::string file
= FileName
+ compressor
->Extension
;
976 if (FileExists(file
) == false)
982 else if (Compress
== Extension
)
984 std::string::size_type
const found
= FileName
.find_last_of('.');
986 if (found
!= std::string::npos
)
988 ext
= FileName
.substr(found
);
989 if (ext
== ".new" || ext
== ".bak")
991 std::string::size_type
const found2
= FileName
.find_last_of('.', found
- 1);
992 if (found2
!= std::string::npos
)
993 ext
= FileName
.substr(found2
, found
- found2
);
998 for (; compressor
!= compressors
.end(); ++compressor
)
999 if (ext
== compressor
->Extension
)
1001 // no matching extension - assume uncompressed (imagine files like 'example.org_Packages')
1002 if (compressor
== compressors
.end())
1003 for (compressor
= compressors
.begin(); compressor
!= compressors
.end(); ++compressor
)
1004 if (compressor
->Name
== ".")
1012 case None
: name
= "."; break;
1013 case Gzip
: name
= "gzip"; break;
1014 case Bzip2
: name
= "bzip2"; break;
1015 case Lzma
: name
= "lzma"; break;
1016 case Xz
: name
= "xz"; break;
1020 return FileFdError("Opening File %s in None, Auto or Extension should be already handled?!?", FileName
.c_str());
1022 for (; compressor
!= compressors
.end(); ++compressor
)
1023 if (compressor
->Name
== name
)
1025 if (compressor
== compressors
.end())
1026 return FileFdError("Can't find a configured compressor %s for file %s", name
.c_str(), FileName
.c_str());
1029 if (compressor
== compressors
.end())
1030 return FileFdError("Can't find a match for specified compressor mode for file %s", FileName
.c_str());
1031 return Open(FileName
, Mode
, *compressor
, AccessMode
);
1033 bool FileFd::Open(string FileName
,unsigned int const Mode
,APT::Configuration::Compressor
const &compressor
, unsigned long const AccessMode
)
1038 if ((Mode
& WriteOnly
) != WriteOnly
&& (Mode
& (Atomic
| Create
| Empty
| Exclusive
)) != 0)
1039 return FileFdError("ReadOnly mode for %s doesn't accept additional flags!", FileName
.c_str());
1040 if ((Mode
& ReadWrite
) == 0)
1041 return FileFdError("No openmode provided in FileFd::Open for %s", FileName
.c_str());
1043 if ((Mode
& Atomic
) == Atomic
)
1047 else if ((Mode
& (Exclusive
| Create
)) == (Exclusive
| Create
))
1049 // for atomic, this will be done by rename in Close()
1050 unlink(FileName
.c_str());
1052 if ((Mode
& Empty
) == Empty
)
1055 if (lstat(FileName
.c_str(),&Buf
) == 0 && S_ISLNK(Buf
.st_mode
))
1056 unlink(FileName
.c_str());
1060 #define if_FLAGGED_SET(FLAG, MODE) if ((Mode & FLAG) == FLAG) fileflags |= MODE
1061 if_FLAGGED_SET(ReadWrite
, O_RDWR
);
1062 else if_FLAGGED_SET(ReadOnly
, O_RDONLY
);
1063 else if_FLAGGED_SET(WriteOnly
, O_WRONLY
);
1065 if_FLAGGED_SET(Create
, O_CREAT
);
1066 if_FLAGGED_SET(Empty
, O_TRUNC
);
1067 if_FLAGGED_SET(Exclusive
, O_EXCL
);
1068 #undef if_FLAGGED_SET
1070 if ((Mode
& Atomic
) == Atomic
)
1072 char *name
= strdup((FileName
+ ".XXXXXX").c_str());
1074 if((iFd
= mkstemp(name
)) == -1)
1077 return FileFdErrno("mkstemp", "Could not create temporary file for %s", FileName
.c_str());
1080 TemporaryFileName
= string(name
);
1083 // umask() will always set the umask and return the previous value, so
1084 // we first set the umask and then reset it to the old value
1085 mode_t
const CurrentUmask
= umask(0);
1086 umask(CurrentUmask
);
1087 // calculate the actual file permissions (just like open/creat)
1088 mode_t
const FilePermissions
= (AccessMode
& ~CurrentUmask
);
1090 if(fchmod(iFd
, FilePermissions
) == -1)
1091 return FileFdErrno("fchmod", "Could not change permissions for temporary file %s", TemporaryFileName
.c_str());
1094 iFd
= open(FileName
.c_str(), fileflags
, AccessMode
);
1096 this->FileName
= FileName
;
1097 if (iFd
== -1 || OpenInternDescriptor(Mode
, compressor
) == false)
1104 return FileFdErrno("open",_("Could not open file %s"), FileName
.c_str());
1107 SetCloseExec(iFd
,true);
1111 // FileFd::OpenDescriptor - Open a filedescriptor /*{{{*/
1112 // ---------------------------------------------------------------------
1114 bool FileFd::OpenDescriptor(int Fd
, unsigned int const Mode
, CompressMode Compress
, bool AutoClose
)
1116 std::vector
<APT::Configuration::Compressor
> const compressors
= APT::Configuration::getCompressors();
1117 std::vector
<APT::Configuration::Compressor
>::const_iterator compressor
= compressors
.begin();
1120 // compat with the old API
1121 if (Mode
== ReadOnlyGzip
&& Compress
== None
)
1126 case None
: name
= "."; break;
1127 case Gzip
: name
= "gzip"; break;
1128 case Bzip2
: name
= "bzip2"; break;
1129 case Lzma
: name
= "lzma"; break;
1130 case Xz
: name
= "xz"; break;
1133 if (AutoClose
== true && Fd
!= -1)
1135 return FileFdError("Opening Fd %d in Auto or Extension compression mode is not supported", Fd
);
1137 for (; compressor
!= compressors
.end(); ++compressor
)
1138 if (compressor
->Name
== name
)
1140 if (compressor
== compressors
.end())
1142 if (AutoClose
== true && Fd
!= -1)
1144 return FileFdError("Can't find a configured compressor %s for file %s", name
.c_str(), FileName
.c_str());
1146 return OpenDescriptor(Fd
, Mode
, *compressor
, AutoClose
);
1148 bool FileFd::OpenDescriptor(int Fd
, unsigned int const Mode
, APT::Configuration::Compressor
const &compressor
, bool AutoClose
)
1151 Flags
= (AutoClose
) ? FileFd::AutoClose
: 0;
1153 this->FileName
= "";
1154 if (OpenInternDescriptor(Mode
, compressor
) == false)
1157 (Flags
& Compressed
) == Compressed
||
1163 return FileFdError(_("Could not open file descriptor %d"), Fd
);
1167 bool FileFd::OpenInternDescriptor(unsigned int const Mode
, APT::Configuration::Compressor
const &compressor
)
1171 if (compressor
.Name
== "." || compressor
.Binary
.empty() == true)
1174 #if defined HAVE_ZLIB || defined HAVE_BZ2 || defined HAVE_LZMA
1175 // the API to open files is similar, so setup to avoid code duplicates later
1176 // and while at it ensure that we close before opening (if its a reopen)
1177 void* (*compress_open
)(int, const char *) = NULL
;
1179 /* dummy so that the rest can be 'else if's */;
1180 #define APT_COMPRESS_INIT(NAME,OPEN) \
1181 else if (compressor.Name == NAME) \
1183 compress_open = (void*(*)(int, const char *)) OPEN; \
1184 if (d != NULL) d->InternalClose(FileName); \
1187 APT_COMPRESS_INIT("gzip", gzdopen
)
1190 APT_COMPRESS_INIT("bzip2", BZ2_bzdopen
)
1193 APT_COMPRESS_INIT("xz", fdopen
)
1194 APT_COMPRESS_INIT("lzma", fdopen
)
1196 #undef APT_COMPRESS_INIT
1201 d
= new FileFdPrivate();
1203 d
->compressor
= compressor
;
1204 #if defined HAVE_ZLIB || defined HAVE_BZ2 || defined HAVE_LZMA
1205 if ((Flags
& AutoClose
) != AutoClose
&& compress_open
!= NULL
)
1207 // Need to duplicate fd here or gz/bz2 close for cleanup will close the fd as well
1208 int const internFd
= dup(iFd
);
1210 return FileFdErrno("OpenInternDescriptor", _("Could not open file descriptor %d"), iFd
);
1216 #if defined HAVE_ZLIB || defined HAVE_BZ2 || defined HAVE_LZMA
1217 if (compress_open
!= NULL
)
1219 void* compress_struct
= NULL
;
1220 if ((Mode
& ReadWrite
) == ReadWrite
)
1221 compress_struct
= compress_open(iFd
, "r+");
1222 else if ((Mode
& WriteOnly
) == WriteOnly
)
1223 compress_struct
= compress_open(iFd
, "w");
1225 compress_struct
= compress_open(iFd
, "r");
1226 if (compress_struct
== NULL
)
1230 /* dummy so that the rest can be 'else if's */;
1232 else if (compressor
.Name
== "gzip")
1233 d
->gz
= (gzFile
) compress_struct
;
1236 else if (compressor
.Name
== "bzip2")
1237 d
->bz2
= (BZFILE
*) compress_struct
;
1240 else if (compressor
.Name
== "xz" || compressor
.Name
== "lzma")
1242 uint32_t const xzlevel
= 6;
1243 uint64_t const memlimit
= UINT64_MAX
;
1244 if (d
->lzma
== NULL
)
1245 d
->lzma
= new FileFdPrivate::LZMAFILE
;
1246 d
->lzma
->file
= (FILE*) compress_struct
;
1247 d
->lzma
->stream
= LZMA_STREAM_INIT
;
1249 if ((Mode
& ReadWrite
) == ReadWrite
)
1250 return FileFdError("ReadWrite mode is not supported for file %s", FileName
.c_str());
1252 if ((Mode
& WriteOnly
) == WriteOnly
)
1254 if (compressor
.Name
== "xz")
1256 if (lzma_easy_encoder(&d
->lzma
->stream
, xzlevel
, LZMA_CHECK_CRC32
) != LZMA_OK
)
1261 lzma_options_lzma options
;
1262 lzma_lzma_preset(&options
, xzlevel
);
1263 if (lzma_alone_encoder(&d
->lzma
->stream
, &options
) != LZMA_OK
)
1266 d
->lzma
->compressing
= true;
1270 if (compressor
.Name
== "xz")
1272 if (lzma_auto_decoder(&d
->lzma
->stream
, memlimit
, 0) != LZMA_OK
)
1277 if (lzma_alone_decoder(&d
->lzma
->stream
, memlimit
) != LZMA_OK
)
1280 d
->lzma
->compressing
= false;
1284 Flags
|= Compressed
;
1289 // collect zombies here in case we reopen
1290 if (d
->compressor_pid
> 0)
1291 ExecWait(d
->compressor_pid
, "FileFdCompressor", true);
1293 if ((Mode
& ReadWrite
) == ReadWrite
)
1294 return FileFdError("ReadWrite mode is not supported for file %s", FileName
.c_str());
1296 bool const Comp
= (Mode
& WriteOnly
) == WriteOnly
;
1299 // Handle 'decompression' of empty files
1302 if (Buf
.st_size
== 0 && S_ISFIFO(Buf
.st_mode
) == false)
1305 // We don't need the file open - instead let the compressor open it
1306 // as he properly knows better how to efficiently read from 'his' file
1307 if (FileName
.empty() == false)
1314 // Create a data pipe
1315 int Pipe
[2] = {-1,-1};
1316 if (pipe(Pipe
) != 0)
1317 return FileFdErrno("pipe",_("Failed to create subprocess IPC"));
1318 for (int J
= 0; J
!= 2; J
++)
1319 SetCloseExec(Pipe
[J
],true);
1321 d
->compressed_fd
= iFd
;
1330 d
->compressor_pid
= ExecFork();
1331 if (d
->compressor_pid
== 0)
1335 dup2(d
->compressed_fd
,STDOUT_FILENO
);
1336 dup2(Pipe
[0],STDIN_FILENO
);
1340 if (d
->compressed_fd
!= -1)
1341 dup2(d
->compressed_fd
,STDIN_FILENO
);
1342 dup2(Pipe
[1],STDOUT_FILENO
);
1344 int const nullfd
= open("/dev/null", O_WRONLY
);
1347 dup2(nullfd
,STDERR_FILENO
);
1351 SetCloseExec(STDOUT_FILENO
,false);
1352 SetCloseExec(STDIN_FILENO
,false);
1354 std::vector
<char const*> Args
;
1355 Args
.push_back(compressor
.Binary
.c_str());
1356 std::vector
<std::string
> const * const addArgs
=
1357 (Comp
== true) ? &(compressor
.CompressArgs
) : &(compressor
.UncompressArgs
);
1358 for (std::vector
<std::string
>::const_iterator a
= addArgs
->begin();
1359 a
!= addArgs
->end(); ++a
)
1360 Args
.push_back(a
->c_str());
1361 if (Comp
== false && FileName
.empty() == false)
1363 // commands not needing arguments, do not need to be told about using standard output
1364 // in reality, only testcases with tools like cat, rev, rot13, … are able to trigger this
1365 if (compressor
.CompressArgs
.empty() == false && compressor
.UncompressArgs
.empty() == false)
1366 Args
.push_back("--stdout");
1367 if (TemporaryFileName
.empty() == false)
1368 Args
.push_back(TemporaryFileName
.c_str());
1370 Args
.push_back(FileName
.c_str());
1372 Args
.push_back(NULL
);
1374 execvp(Args
[0],(char **)&Args
[0]);
1375 cerr
<< _("Failed to exec compressor ") << Args
[0] << endl
;
1386 // FileFd::~File - Closes the file /*{{{*/
1387 // ---------------------------------------------------------------------
1388 /* If the proper modes are selected then we close the Fd and possibly
1389 unlink the file on error. */
1394 d
->CloseDown(FileName
);
1399 // FileFd::Read - Read a bit of the file /*{{{*/
1400 // ---------------------------------------------------------------------
1401 /* We are careful to handle interruption by a signal while reading
1403 bool FileFd::Read(void *To
,unsigned long long Size
,unsigned long long *Actual
)
1409 *((char *)To
) = '\0';
1413 /* dummy so that the rest can be 'else if's */;
1415 else if (d
!= NULL
&& d
->gz
!= NULL
)
1416 Res
= gzread(d
->gz
,To
,Size
);
1419 else if (d
!= NULL
&& d
->bz2
!= NULL
)
1420 Res
= BZ2_bzread(d
->bz2
,To
,Size
);
1423 else if (d
!= NULL
&& d
->lzma
!= NULL
)
1425 if (d
->lzma
->eof
== true)
1428 d
->lzma
->stream
.next_out
= (uint8_t *) To
;
1429 d
->lzma
->stream
.avail_out
= Size
;
1430 if (d
->lzma
->stream
.avail_in
== 0)
1432 d
->lzma
->stream
.next_in
= d
->lzma
->buffer
;
1433 d
->lzma
->stream
.avail_in
= fread(d
->lzma
->buffer
, 1, sizeof(d
->lzma
->buffer
)/sizeof(d
->lzma
->buffer
[0]), d
->lzma
->file
);
1435 d
->lzma
->err
= lzma_code(&d
->lzma
->stream
, LZMA_RUN
);
1436 if (d
->lzma
->err
== LZMA_STREAM_END
)
1438 d
->lzma
->eof
= true;
1439 Res
= Size
- d
->lzma
->stream
.avail_out
;
1441 else if (d
->lzma
->err
!= LZMA_OK
)
1448 Res
= Size
- d
->lzma
->stream
.avail_out
;
1451 // lzma run was okay, but produced no output…
1459 Res
= read(iFd
,To
,Size
);
1465 // trick the while-loop into running again
1471 /* dummy so that the rest can be 'else if's */;
1473 else if (d
!= NULL
&& d
->gz
!= NULL
)
1476 char const * const errmsg
= gzerror(d
->gz
, &err
);
1478 return FileFdError("gzread: %s (%d: %s)", _("Read error"), err
, errmsg
);
1482 else if (d
!= NULL
&& d
->bz2
!= NULL
)
1485 char const * const errmsg
= BZ2_bzerror(d
->bz2
, &err
);
1486 if (err
!= BZ_IO_ERROR
)
1487 return FileFdError("BZ2_bzread: %s (%d: %s)", _("Read error"), err
, errmsg
);
1491 else if (d
!= NULL
&& d
->lzma
!= NULL
)
1492 return FileFdError("lzma_read: %s (%d)", _("Read error"), d
->lzma
->err
);
1494 return FileFdErrno("read",_("Read error"));
1497 To
= (char *)To
+ Res
;
1504 while (Res
> 0 && Size
> 0);
1516 return FileFdError(_("read, still have %llu to read but none left"), Size
);
1519 // FileFd::ReadLine - Read a complete line from the file /*{{{*/
1520 // ---------------------------------------------------------------------
1521 /* Beware: This method can be quiet slow for big buffers on UNcompressed
1522 files because of the naive implementation! */
1523 char* FileFd::ReadLine(char *To
, unsigned long long const Size
)
1527 if (d
!= NULL
&& d
->gz
!= NULL
)
1528 return gzgets(d
->gz
, To
, Size
);
1531 unsigned long long read
= 0;
1532 while ((Size
- 1) != read
)
1534 unsigned long long done
= 0;
1535 if (Read(To
+ read
, 1, &done
) == false)
1539 if (To
[read
++] == '\n')
1548 // FileFd::Write - Write to the file /*{{{*/
1549 // ---------------------------------------------------------------------
1551 bool FileFd::Write(const void *From
,unsigned long long Size
)
1558 /* dummy so that the rest can be 'else if's */;
1560 else if (d
!= NULL
&& d
->gz
!= NULL
)
1561 Res
= gzwrite(d
->gz
,From
,Size
);
1564 else if (d
!= NULL
&& d
->bz2
!= NULL
)
1565 Res
= BZ2_bzwrite(d
->bz2
,(void*)From
,Size
);
1568 else if (d
!= NULL
&& d
->lzma
!= NULL
)
1570 d
->lzma
->stream
.next_in
= (uint8_t *)From
;
1571 d
->lzma
->stream
.avail_in
= Size
;
1572 d
->lzma
->stream
.next_out
= d
->lzma
->buffer
;
1573 d
->lzma
->stream
.avail_out
= sizeof(d
->lzma
->buffer
)/sizeof(d
->lzma
->buffer
[0]);
1574 d
->lzma
->err
= lzma_code(&d
->lzma
->stream
, LZMA_RUN
);
1575 if (d
->lzma
->err
!= LZMA_OK
)
1577 size_t const n
= sizeof(d
->lzma
->buffer
)/sizeof(d
->lzma
->buffer
[0]) - d
->lzma
->stream
.avail_out
;
1578 size_t const m
= (n
== 0) ? 0 : fwrite(d
->lzma
->buffer
, 1, n
, d
->lzma
->file
);
1582 Res
= Size
- d
->lzma
->stream
.avail_in
;
1586 Res
= write(iFd
,From
,Size
);
1588 if (Res
< 0 && errno
== EINTR
)
1593 /* dummy so that the rest can be 'else if's */;
1595 else if (d
!= NULL
&& d
->gz
!= NULL
)
1598 char const * const errmsg
= gzerror(d
->gz
, &err
);
1600 return FileFdError("gzwrite: %s (%d: %s)", _("Write error"), err
, errmsg
);
1604 else if (d
!= NULL
&& d
->bz2
!= NULL
)
1607 char const * const errmsg
= BZ2_bzerror(d
->bz2
, &err
);
1608 if (err
!= BZ_IO_ERROR
)
1609 return FileFdError("BZ2_bzwrite: %s (%d: %s)", _("Write error"), err
, errmsg
);
1613 else if (d
!= NULL
&& d
->lzma
!= NULL
)
1614 return FileFdErrno("lzma_fwrite", _("Write error"));
1616 return FileFdErrno("write",_("Write error"));
1619 From
= (char const *)From
+ Res
;
1624 while (Res
> 0 && Size
> 0);
1629 return FileFdError(_("write, still have %llu to write but couldn't"), Size
);
1631 bool FileFd::Write(int Fd
, const void *From
, unsigned long long Size
)
1637 Res
= write(Fd
,From
,Size
);
1638 if (Res
< 0 && errno
== EINTR
)
1641 return _error
->Errno("write",_("Write error"));
1643 From
= (char const *)From
+ Res
;
1646 while (Res
> 0 && Size
> 0);
1651 return _error
->Error(_("write, still have %llu to write but couldn't"), Size
);
1654 // FileFd::Seek - Seek in the file /*{{{*/
1655 // ---------------------------------------------------------------------
1657 bool FileFd::Seek(unsigned long long To
)
1661 if (d
!= NULL
&& (d
->pipe
== true || d
->InternalStream() == true))
1663 // Our poor man seeking in pipes is costly, so try to avoid it
1664 unsigned long long seekpos
= Tell();
1667 else if (seekpos
< To
)
1668 return Skip(To
- seekpos
);
1670 if ((d
->openmode
& ReadOnly
) != ReadOnly
)
1671 return FileFdError("Reopen is only implemented for read-only files!");
1672 d
->InternalClose(FileName
);
1676 if (TemporaryFileName
.empty() == false)
1677 iFd
= open(TemporaryFileName
.c_str(), O_RDONLY
);
1678 else if (FileName
.empty() == false)
1679 iFd
= open(FileName
.c_str(), O_RDONLY
);
1682 if (d
->compressed_fd
> 0)
1683 if (lseek(d
->compressed_fd
, 0, SEEK_SET
) != 0)
1684 iFd
= d
->compressed_fd
;
1686 return FileFdError("Reopen is not implemented for pipes opened with FileFd::OpenDescriptor()!");
1689 if (OpenInternDescriptor(d
->openmode
, d
->compressor
) == false)
1690 return FileFdError("Seek on file %s because it couldn't be reopened", FileName
.c_str());
1700 if (d
!= NULL
&& d
->gz
)
1701 res
= gzseek(d
->gz
,To
,SEEK_SET
);
1704 res
= lseek(iFd
,To
,SEEK_SET
);
1705 if (res
!= (off_t
)To
)
1706 return FileFdError("Unable to seek to %llu", To
);
1713 // FileFd::Skip - Seek in the file /*{{{*/
1714 // ---------------------------------------------------------------------
1716 bool FileFd::Skip(unsigned long long Over
)
1718 if (d
!= NULL
&& (d
->pipe
== true || d
->InternalStream() == true))
1723 unsigned long long toread
= std::min((unsigned long long) sizeof(buffer
), Over
);
1724 if (Read(buffer
, toread
) == false)
1725 return FileFdError("Unable to seek ahead %llu",Over
);
1733 if (d
!= NULL
&& d
->gz
!= NULL
)
1734 res
= gzseek(d
->gz
,Over
,SEEK_CUR
);
1737 res
= lseek(iFd
,Over
,SEEK_CUR
);
1739 return FileFdError("Unable to seek ahead %llu",Over
);
1746 // FileFd::Truncate - Truncate the file /*{{{*/
1747 // ---------------------------------------------------------------------
1749 bool FileFd::Truncate(unsigned long long To
)
1751 // truncating /dev/null is always successful - as we get an error otherwise
1752 if (To
== 0 && FileName
== "/dev/null")
1754 #if defined HAVE_ZLIB || defined HAVE_BZ2 || defined HAVE_LZMA
1755 if (d
!= NULL
&& (d
->InternalStream() == true
1760 return FileFdError("Truncating compressed files is not implemented (%s)", FileName
.c_str());
1762 if (ftruncate(iFd
,To
) != 0)
1763 return FileFdError("Unable to truncate to %llu",To
);
1768 // FileFd::Tell - Current seek position /*{{{*/
1769 // ---------------------------------------------------------------------
1771 unsigned long long FileFd::Tell()
1773 // In theory, we could just return seekpos here always instead of
1774 // seeking around, but not all users of FileFd use always Seek() and co
1775 // so d->seekpos isn't always true and we can just use it as a hint if
1776 // we have nothing else, but not always as an authority…
1777 if (d
!= NULL
&& (d
->pipe
== true || d
->InternalStream() == true))
1782 if (d
!= NULL
&& d
->gz
!= NULL
)
1783 Res
= gztell(d
->gz
);
1786 Res
= lseek(iFd
,0,SEEK_CUR
);
1787 if (Res
== (off_t
)-1)
1788 FileFdErrno("lseek","Failed to determine the current file position");
1794 static bool StatFileFd(char const * const msg
, int const iFd
, std::string
const &FileName
, struct stat
&Buf
, FileFdPrivate
* const d
) /*{{{*/
1796 bool ispipe
= (d
!= NULL
&& d
->pipe
== true);
1797 if (ispipe
== false)
1799 if (fstat(iFd
,&Buf
) != 0)
1800 // higher-level code will generate more meaningful messages,
1801 // even translated this would be meaningless for users
1802 return _error
->Errno("fstat", "Unable to determine %s for fd %i", msg
, iFd
);
1803 ispipe
= S_ISFIFO(Buf
.st_mode
);
1806 // for compressor pipes st_size is undefined and at 'best' zero
1809 // we set it here, too, as we get the info here for free
1810 // in theory the Open-methods should take care of it already
1813 if (stat(FileName
.c_str(), &Buf
) != 0)
1814 return _error
->Errno("fstat", "Unable to determine %s for file %s", msg
, FileName
.c_str());
1819 // FileFd::FileSize - Return the size of the file /*{{{*/
1820 unsigned long long FileFd::FileSize()
1823 if (StatFileFd("file size", iFd
, FileName
, Buf
, d
) == false)
1831 // FileFd::ModificationTime - Return the time of last touch /*{{{*/
1832 time_t FileFd::ModificationTime()
1835 if (StatFileFd("modification time", iFd
, FileName
, Buf
, d
) == false)
1840 return Buf
.st_mtime
;
1843 // FileFd::Size - Return the size of the content in the file /*{{{*/
1844 // ---------------------------------------------------------------------
1846 unsigned long long FileFd::Size()
1848 unsigned long long size
= FileSize();
1850 // for compressor pipes st_size is undefined and at 'best' zero,
1851 // so we 'read' the content and 'seek' back - see there
1852 if (d
!= NULL
&& (d
->pipe
== true || (d
->InternalStream() == true && size
> 0)))
1854 unsigned long long const oldSeek
= Tell();
1856 unsigned long long read
= 0;
1858 if (Read(ignore
, sizeof(ignore
), &read
) == false)
1868 // only check gzsize if we are actually a gzip file, just checking for
1869 // "gz" is not sufficient as uncompressed files could be opened with
1870 // gzopen in "direct" mode as well
1871 else if (d
!= NULL
&& d
->gz
&& !gzdirect(d
->gz
) && size
> 0)
1873 off_t
const oldPos
= lseek(iFd
,0,SEEK_CUR
);
1874 /* unfortunately zlib.h doesn't provide a gzsize(), so we have to do
1875 * this ourselves; the original (uncompressed) file size is the last 32
1876 * bits of the file */
1877 // FIXME: Size for gz-files is limited by 32bit… no largefile support
1878 if (lseek(iFd
, -4, SEEK_END
) < 0)
1880 FileFdErrno("lseek","Unable to seek to end of gzipped file");
1884 if (read(iFd
, &size
, 4) != 4)
1886 FileFdErrno("read","Unable to read original size of gzipped file");
1890 #ifdef WORDS_BIGENDIAN
1891 uint32_t tmp_size
= size
;
1892 uint8_t const * const p
= (uint8_t const * const) &tmp_size
;
1893 tmp_size
= (p
[3] << 24) | (p
[2] << 16) | (p
[1] << 8) | p
[0];
1897 if (lseek(iFd
, oldPos
, SEEK_SET
) < 0)
1899 FileFdErrno("lseek","Unable to seek in gzipped file");
1910 // FileFd::Close - Close the file if the close flag is set /*{{{*/
1911 // ---------------------------------------------------------------------
1913 bool FileFd::Close()
1919 if ((Flags
& AutoClose
) == AutoClose
)
1921 if ((Flags
& Compressed
) != Compressed
&& iFd
> 0 && close(iFd
) != 0)
1922 Res
&= _error
->Errno("close",_("Problem closing the file %s"), FileName
.c_str());
1926 Res
&= d
->CloseDown(FileName
);
1932 if ((Flags
& Replace
) == Replace
) {
1933 if (rename(TemporaryFileName
.c_str(), FileName
.c_str()) != 0)
1934 Res
&= _error
->Errno("rename",_("Problem renaming the file %s to %s"), TemporaryFileName
.c_str(), FileName
.c_str());
1936 FileName
= TemporaryFileName
; // for the unlink() below.
1937 TemporaryFileName
.clear();
1942 if ((Flags
& Fail
) == Fail
&& (Flags
& DelOnFail
) == DelOnFail
&&
1943 FileName
.empty() == false)
1944 if (unlink(FileName
.c_str()) != 0)
1945 Res
&= _error
->WarningE("unlnk",_("Problem unlinking the file %s"), FileName
.c_str());
1952 // FileFd::Sync - Sync the file /*{{{*/
1953 // ---------------------------------------------------------------------
1957 if (fsync(iFd
) != 0)
1958 return FileFdErrno("sync",_("Problem syncing the file"));
1962 // FileFd::FileFdErrno - set Fail and call _error->Errno *{{{*/
1963 bool FileFd::FileFdErrno(const char *Function
, const char *Description
,...)
1967 size_t msgSize
= 400;
1968 int const errsv
= errno
;
1971 va_start(args
,Description
);
1972 if (_error
->InsertErrno(GlobalError::ERROR
, Function
, Description
, args
, errsv
, msgSize
) == false)
1979 // FileFd::FileFdError - set Fail and call _error->Error *{{{*/
1980 bool FileFd::FileFdError(const char *Description
,...) {
1983 size_t msgSize
= 400;
1986 va_start(args
,Description
);
1987 if (_error
->Insert(GlobalError::ERROR
, Description
, args
, msgSize
) == false)
1995 APT_DEPRECATED gzFile
FileFd::gzFd() {
2004 // Glob - wrapper around "glob()" /*{{{*/
2005 // ---------------------------------------------------------------------
2007 std::vector
<std::string
> Glob(std::string
const &pattern
, int flags
)
2009 std::vector
<std::string
> result
;
2014 glob_res
= glob(pattern
.c_str(), flags
, NULL
, &globbuf
);
2018 if(glob_res
!= GLOB_NOMATCH
) {
2019 _error
->Errno("glob", "Problem with glob");
2025 for(i
=0;i
<globbuf
.gl_pathc
;i
++)
2026 result
.push_back(string(globbuf
.gl_pathv
[i
]));
2033 std::string
GetTempDir()
2035 const char *tmpdir
= getenv("TMPDIR");
2042 // check that tmpdir is set and exists
2044 if (!tmpdir
|| strlen(tmpdir
) == 0 || stat(tmpdir
, &st
) != 0)
2047 return string(tmpdir
);
2050 bool Rename(std::string From
, std::string To
)
2052 if (rename(From
.c_str(),To
.c_str()) != 0)
2054 _error
->Error(_("rename failed, %s (%s -> %s)."),strerror(errno
),
2055 From
.c_str(),To
.c_str());