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>
71 // RunScripts - Run a set of scripts from a configuration subtree /*{{{*/
72 // ---------------------------------------------------------------------
74 bool RunScripts(const char *Cnf
)
76 Configuration::Item
const *Opts
= _config
->Tree(Cnf
);
77 if (Opts
== 0 || Opts
->Child
== 0)
81 // Fork for running the system calls
82 pid_t Child
= ExecFork();
87 if (_config
->FindDir("DPkg::Chroot-Directory","/") != "/")
89 std::cerr
<< "Chrooting into "
90 << _config
->FindDir("DPkg::Chroot-Directory")
92 if (chroot(_config
->FindDir("DPkg::Chroot-Directory","/").c_str()) != 0)
96 if (chdir("/tmp/") != 0)
99 unsigned int Count
= 1;
100 for (; Opts
!= 0; Opts
= Opts
->Next
, Count
++)
102 if (Opts
->Value
.empty() == true)
105 if(_config
->FindB("Debug::RunScripts", false) == true)
106 std::clog
<< "Running external script: '"
107 << Opts
->Value
<< "'" << std::endl
;
109 if (system(Opts
->Value
.c_str()) != 0)
115 // Wait for the child
117 while (waitpid(Child
,&Status
,0) != Child
)
121 return _error
->Errno("waitpid","Couldn't wait for subprocess");
124 // Restore sig int/quit
125 signal(SIGQUIT
,SIG_DFL
);
126 signal(SIGINT
,SIG_DFL
);
128 // Check for an error code.
129 if (WIFEXITED(Status
) == 0 || WEXITSTATUS(Status
) != 0)
131 unsigned int Count
= WEXITSTATUS(Status
);
135 for (; Opts
!= 0 && Count
!= 1; Opts
= Opts
->Next
, Count
--);
136 _error
->Error("Problem executing scripts %s '%s'",Cnf
,Opts
->Value
.c_str());
139 return _error
->Error("Sub-process returned an error code");
146 // CopyFile - Buffered copy of a file /*{{{*/
147 // ---------------------------------------------------------------------
148 /* The caller is expected to set things so that failure causes erasure */
149 bool CopyFile(FileFd
&From
,FileFd
&To
)
151 if (From
.IsOpen() == false || To
.IsOpen() == false ||
152 From
.Failed() == true || To
.Failed() == true)
155 // Buffered copy between fds
156 SPtrArray
<unsigned char> Buf
= new unsigned char[64000];
157 unsigned long long Size
= From
.Size();
160 unsigned long long ToRead
= Size
;
164 if (From
.Read(Buf
,ToRead
) == false ||
165 To
.Write(Buf
,ToRead
) == false)
174 // GetLock - Gets a lock file /*{{{*/
175 // ---------------------------------------------------------------------
176 /* This will create an empty file of the given name and lock it. Once this
177 is done all other calls to GetLock in any other process will fail with
178 -1. The return result is the fd of the file, the call should call
179 close at some time. */
180 int GetLock(string File
,bool Errors
)
182 // GetLock() is used in aptitude on directories with public-write access
183 // Use O_NOFOLLOW here to prevent symlink traversal attacks
184 int FD
= open(File
.c_str(),O_RDWR
| O_CREAT
| O_NOFOLLOW
,0640);
187 // Read only .. can't have locking problems there.
190 _error
->Warning(_("Not using locking for read only lock file %s"),File
.c_str());
191 return dup(0); // Need something for the caller to close
195 _error
->Errno("open",_("Could not open lock file %s"),File
.c_str());
197 // Feh.. We do this to distinguish the lock vs open case..
201 SetCloseExec(FD
,true);
203 // Acquire a write lock
206 fl
.l_whence
= SEEK_SET
;
209 if (fcntl(FD
,F_SETLK
,&fl
) == -1)
211 // always close to not leak resources
218 _error
->Warning(_("Not using locking for nfs mounted lock file %s"),File
.c_str());
219 return dup(0); // Need something for the caller to close
223 _error
->Errno("open",_("Could not get lock %s"),File
.c_str());
231 // FileExists - Check if a file exists /*{{{*/
232 // ---------------------------------------------------------------------
233 /* Beware: Directories are also files! */
234 bool FileExists(string File
)
237 if (stat(File
.c_str(),&Buf
) != 0)
242 // RealFileExists - Check if a file exists and if it is really a file /*{{{*/
243 // ---------------------------------------------------------------------
245 bool RealFileExists(string File
)
248 if (stat(File
.c_str(),&Buf
) != 0)
250 return ((Buf
.st_mode
& S_IFREG
) != 0);
253 // DirectoryExists - Check if a directory exists and is really one /*{{{*/
254 // ---------------------------------------------------------------------
256 bool DirectoryExists(string
const &Path
)
259 if (stat(Path
.c_str(),&Buf
) != 0)
261 return ((Buf
.st_mode
& S_IFDIR
) != 0);
264 // CreateDirectory - poor man's mkdir -p guarded by a parent directory /*{{{*/
265 // ---------------------------------------------------------------------
266 /* This method will create all directories needed for path in good old
267 mkdir -p style but refuses to do this if Parent is not a prefix of
268 this Path. Example: /var/cache/ and /var/cache/apt/archives are given,
269 so it will create apt/archives if /var/cache exists - on the other
270 hand if the parent is /var/lib the creation will fail as this path
271 is not a parent of the path to be generated. */
272 bool CreateDirectory(string
const &Parent
, string
const &Path
)
274 if (Parent
.empty() == true || Path
.empty() == true)
277 if (DirectoryExists(Path
) == true)
280 if (DirectoryExists(Parent
) == false)
283 // we are not going to create directories "into the blue"
284 if (Path
.compare(0, Parent
.length(), Parent
) != 0)
287 vector
<string
> const dirs
= VectorizeString(Path
.substr(Parent
.size()), '/');
288 string progress
= Parent
;
289 for (vector
<string
>::const_iterator d
= dirs
.begin(); d
!= dirs
.end(); ++d
)
291 if (d
->empty() == true)
294 progress
.append("/").append(*d
);
295 if (DirectoryExists(progress
) == true)
298 if (mkdir(progress
.c_str(), 0755) != 0)
304 // CreateAPTDirectoryIfNeeded - ensure that the given directory exists /*{{{*/
305 // ---------------------------------------------------------------------
306 /* a small wrapper around CreateDirectory to check if it exists and to
307 remove the trailing "/apt/" from the parent directory if needed */
308 bool CreateAPTDirectoryIfNeeded(string
const &Parent
, string
const &Path
)
310 if (DirectoryExists(Path
) == true)
313 size_t const len
= Parent
.size();
314 if (len
> 5 && Parent
.find("/apt/", len
- 6, 5) == len
- 5)
316 if (CreateDirectory(Parent
.substr(0,len
-5), Path
) == true)
319 else if (CreateDirectory(Parent
, Path
) == true)
325 // GetListOfFilesInDir - returns a vector of files in the given dir /*{{{*/
326 // ---------------------------------------------------------------------
327 /* If an extension is given only files with this extension are included
328 in the returned vector, otherwise every "normal" file is included. */
329 std::vector
<string
> GetListOfFilesInDir(string
const &Dir
, string
const &Ext
,
330 bool const &SortList
, bool const &AllowNoExt
)
332 std::vector
<string
> ext
;
334 if (Ext
.empty() == false)
336 if (AllowNoExt
== true && ext
.empty() == false)
338 return GetListOfFilesInDir(Dir
, ext
, SortList
);
340 std::vector
<string
> GetListOfFilesInDir(string
const &Dir
, std::vector
<string
> const &Ext
,
341 bool const &SortList
)
343 // Attention debuggers: need to be set with the environment config file!
344 bool const Debug
= _config
->FindB("Debug::GetListOfFilesInDir", false);
347 std::clog
<< "Accept in " << Dir
<< " only files with the following " << Ext
.size() << " extensions:" << std::endl
;
348 if (Ext
.empty() == true)
349 std::clog
<< "\tNO extension" << std::endl
;
351 for (std::vector
<string
>::const_iterator e
= Ext
.begin();
353 std::clog
<< '\t' << (e
->empty() == true ? "NO" : *e
) << " extension" << std::endl
;
356 std::vector
<string
> List
;
358 if (DirectoryExists(Dir
) == false)
360 _error
->Error(_("List of files can't be created as '%s' is not a directory"), Dir
.c_str());
364 Configuration::MatchAgainstConfig
SilentIgnore("Dir::Ignore-Files-Silently");
365 DIR *D
= opendir(Dir
.c_str());
368 _error
->Errno("opendir",_("Unable to read %s"),Dir
.c_str());
372 for (struct dirent
*Ent
= readdir(D
); Ent
!= 0; Ent
= readdir(D
))
374 // skip "hidden" files
375 if (Ent
->d_name
[0] == '.')
378 // Make sure it is a file and not something else
379 string
const File
= flCombine(Dir
,Ent
->d_name
);
380 #ifdef _DIRENT_HAVE_D_TYPE
381 if (Ent
->d_type
!= DT_REG
)
384 if (RealFileExists(File
) == false)
386 // do not show ignoration warnings for directories
388 #ifdef _DIRENT_HAVE_D_TYPE
389 Ent
->d_type
== DT_DIR
||
391 DirectoryExists(File
) == true)
393 if (SilentIgnore
.Match(Ent
->d_name
) == false)
394 _error
->Notice(_("Ignoring '%s' in directory '%s' as it is not a regular file"), Ent
->d_name
, Dir
.c_str());
399 // check for accepted extension:
400 // no extension given -> periods are bad as hell!
401 // extensions given -> "" extension allows no extension
402 if (Ext
.empty() == false)
404 string d_ext
= flExtension(Ent
->d_name
);
405 if (d_ext
== Ent
->d_name
) // no extension
407 if (std::find(Ext
.begin(), Ext
.end(), "") == Ext
.end())
410 std::clog
<< "Bad file: " << Ent
->d_name
<< " → no extension" << std::endl
;
411 if (SilentIgnore
.Match(Ent
->d_name
) == false)
412 _error
->Notice(_("Ignoring file '%s' in directory '%s' as it has no filename extension"), Ent
->d_name
, Dir
.c_str());
416 else if (std::find(Ext
.begin(), Ext
.end(), d_ext
) == Ext
.end())
419 std::clog
<< "Bad file: " << Ent
->d_name
<< " → bad extension »" << flExtension(Ent
->d_name
) << "«" << std::endl
;
420 if (SilentIgnore
.Match(Ent
->d_name
) == false)
421 _error
->Notice(_("Ignoring file '%s' in directory '%s' as it has an invalid filename extension"), Ent
->d_name
, Dir
.c_str());
426 // Skip bad filenames ala run-parts
427 const char *C
= Ent
->d_name
;
429 if (isalpha(*C
) == 0 && isdigit(*C
) == 0
430 && *C
!= '_' && *C
!= '-' && *C
!= ':') {
431 // no required extension -> dot is a bad character
432 if (*C
== '.' && Ext
.empty() == false)
437 // we don't reach the end of the name -> bad character included
441 std::clog
<< "Bad file: " << Ent
->d_name
<< " → bad character »"
442 << *C
<< "« in filename (period allowed: " << (Ext
.empty() ? "no" : "yes") << ")" << std::endl
;
446 // skip filenames which end with a period. These are never valid
450 std::clog
<< "Bad file: " << Ent
->d_name
<< " → Period as last character" << std::endl
;
455 std::clog
<< "Accept file: " << Ent
->d_name
<< " in " << Dir
<< std::endl
;
456 List
.push_back(File
);
460 if (SortList
== true)
461 std::sort(List
.begin(),List
.end());
464 std::vector
<string
> GetListOfFilesInDir(string
const &Dir
, bool SortList
)
466 bool const Debug
= _config
->FindB("Debug::GetListOfFilesInDir", false);
468 std::clog
<< "Accept in " << Dir
<< " all regular files" << std::endl
;
470 std::vector
<string
> List
;
472 if (DirectoryExists(Dir
) == false)
474 _error
->Error(_("List of files can't be created as '%s' is not a directory"), Dir
.c_str());
478 DIR *D
= opendir(Dir
.c_str());
481 _error
->Errno("opendir",_("Unable to read %s"),Dir
.c_str());
485 for (struct dirent
*Ent
= readdir(D
); Ent
!= 0; Ent
= readdir(D
))
487 // skip "hidden" files
488 if (Ent
->d_name
[0] == '.')
491 // Make sure it is a file and not something else
492 string
const File
= flCombine(Dir
,Ent
->d_name
);
493 #ifdef _DIRENT_HAVE_D_TYPE
494 if (Ent
->d_type
!= DT_REG
)
497 if (RealFileExists(File
) == false)
500 std::clog
<< "Bad file: " << Ent
->d_name
<< " → it is not a real file" << std::endl
;
505 // Skip bad filenames ala run-parts
506 const char *C
= Ent
->d_name
;
508 if (isalpha(*C
) == 0 && isdigit(*C
) == 0
509 && *C
!= '_' && *C
!= '-' && *C
!= '.')
512 // we don't reach the end of the name -> bad character included
516 std::clog
<< "Bad file: " << Ent
->d_name
<< " → bad character »" << *C
<< "« in filename" << std::endl
;
520 // skip filenames which end with a period. These are never valid
524 std::clog
<< "Bad file: " << Ent
->d_name
<< " → Period as last character" << std::endl
;
529 std::clog
<< "Accept file: " << Ent
->d_name
<< " in " << Dir
<< std::endl
;
530 List
.push_back(File
);
534 if (SortList
== true)
535 std::sort(List
.begin(),List
.end());
539 // SafeGetCWD - This is a safer getcwd that returns a dynamic string /*{{{*/
540 // ---------------------------------------------------------------------
541 /* We return / on failure. */
544 // Stash the current dir.
547 if (getcwd(S
,sizeof(S
)-2) == 0)
549 unsigned int Len
= strlen(S
);
555 // GetModificationTime - Get the mtime of the given file or -1 on error /*{{{*/
556 // ---------------------------------------------------------------------
557 /* We return / on failure. */
558 time_t GetModificationTime(string
const &Path
)
561 if (stat(Path
.c_str(), &St
) < 0)
566 // flNotDir - Strip the directory from the filename /*{{{*/
567 // ---------------------------------------------------------------------
569 string
flNotDir(string File
)
571 string::size_type Res
= File
.rfind('/');
572 if (Res
== string::npos
)
575 return string(File
,Res
,Res
- File
.length());
578 // flNotFile - Strip the file from the directory name /*{{{*/
579 // ---------------------------------------------------------------------
580 /* Result ends in a / */
581 string
flNotFile(string File
)
583 string::size_type Res
= File
.rfind('/');
584 if (Res
== string::npos
)
587 return string(File
,0,Res
);
590 // flExtension - Return the extension for the file /*{{{*/
591 // ---------------------------------------------------------------------
593 string
flExtension(string File
)
595 string::size_type Res
= File
.rfind('.');
596 if (Res
== string::npos
)
599 return string(File
,Res
,Res
- File
.length());
602 // flNoLink - If file is a symlink then deref it /*{{{*/
603 // ---------------------------------------------------------------------
604 /* If the name is not a link then the returned path is the input. */
605 string
flNoLink(string File
)
608 if (lstat(File
.c_str(),&St
) != 0 || S_ISLNK(St
.st_mode
) == 0)
610 if (stat(File
.c_str(),&St
) != 0)
613 /* Loop resolving the link. There is no need to limit the number of
614 loops because the stat call above ensures that the symlink is not
622 if ((Res
= readlink(NFile
.c_str(),Buffer
,sizeof(Buffer
))) <= 0 ||
623 (size_t)Res
>= sizeof(Buffer
))
626 // Append or replace the previous path
628 if (Buffer
[0] == '/')
631 NFile
= flNotFile(NFile
) + Buffer
;
633 // See if we are done
634 if (lstat(NFile
.c_str(),&St
) != 0)
636 if (S_ISLNK(St
.st_mode
) == 0)
641 // flCombine - Combine a file and a directory /*{{{*/
642 // ---------------------------------------------------------------------
643 /* If the file is an absolute path then it is just returned, otherwise
644 the directory is pre-pended to it. */
645 string
flCombine(string Dir
,string File
)
647 if (File
.empty() == true)
650 if (File
[0] == '/' || Dir
.empty() == true)
652 if (File
.length() >= 2 && File
[0] == '.' && File
[1] == '/')
654 if (Dir
[Dir
.length()-1] == '/')
656 return Dir
+ '/' + File
;
659 // flAbsPath - Return the absolute path of the filename /*{{{*/
660 // ---------------------------------------------------------------------
662 string
flAbsPath(string File
)
664 char *p
= realpath(File
.c_str(), NULL
);
667 _error
->Errno("realpath", "flAbsPath failed");
670 std::string
AbsPath(p
);
675 // SetCloseExec - Set the close on exec flag /*{{{*/
676 // ---------------------------------------------------------------------
678 void SetCloseExec(int Fd
,bool Close
)
680 if (fcntl(Fd
,F_SETFD
,(Close
== false)?0:FD_CLOEXEC
) != 0)
682 cerr
<< "FATAL -> Could not set close on exec " << strerror(errno
) << endl
;
687 // SetNonBlock - Set the nonblocking flag /*{{{*/
688 // ---------------------------------------------------------------------
690 void SetNonBlock(int Fd
,bool Block
)
692 int Flags
= fcntl(Fd
,F_GETFL
) & (~O_NONBLOCK
);
693 if (fcntl(Fd
,F_SETFL
,Flags
| ((Block
== false)?0:O_NONBLOCK
)) != 0)
695 cerr
<< "FATAL -> Could not set non-blocking flag " << strerror(errno
) << endl
;
700 // WaitFd - Wait for a FD to become readable /*{{{*/
701 // ---------------------------------------------------------------------
702 /* This waits for a FD to become readable using select. It is useful for
703 applications making use of non-blocking sockets. The timeout is
705 bool WaitFd(int Fd
,bool write
,unsigned long timeout
)
718 Res
= select(Fd
+1,0,&Set
,0,(timeout
!= 0?&tv
:0));
720 while (Res
< 0 && errno
== EINTR
);
730 Res
= select(Fd
+1,&Set
,0,0,(timeout
!= 0?&tv
:0));
732 while (Res
< 0 && errno
== EINTR
);
741 // MergeKeepFdsFromConfiguration - Merge APT::Keep-Fds configuration /*{{{*/
742 // ---------------------------------------------------------------------
743 /* This is used to merge the APT::Keep-Fds with the provided KeepFDs
746 void MergeKeepFdsFromConfiguration(std::set
<int> &KeepFDs
)
748 Configuration::Item
const *Opts
= _config
->Tree("APT::Keep-Fds");
749 if (Opts
!= 0 && Opts
->Child
!= 0)
752 for (; Opts
!= 0; Opts
= Opts
->Next
)
754 if (Opts
->Value
.empty() == true)
756 int fd
= atoi(Opts
->Value
.c_str());
762 // ExecFork - Magical fork that sanitizes the context before execing /*{{{*/
763 // ---------------------------------------------------------------------
764 /* This is used if you want to cleanse the environment for the forked
765 child, it fixes up the important signals and nukes all of the fds,
766 otherwise acts like normal fork. */
770 // we need to merge the Keep-Fds as external tools like
771 // debconf-apt-progress use it
772 MergeKeepFdsFromConfiguration(KeepFDs
);
773 return ExecFork(KeepFDs
);
776 pid_t
ExecFork(std::set
<int> KeepFDs
)
778 // Fork off the process
779 pid_t Process
= fork();
782 cerr
<< "FATAL -> Failed to fork." << endl
;
786 // Spawn the subprocess
790 signal(SIGPIPE
,SIG_DFL
);
791 signal(SIGQUIT
,SIG_DFL
);
792 signal(SIGINT
,SIG_DFL
);
793 signal(SIGWINCH
,SIG_DFL
);
794 signal(SIGCONT
,SIG_DFL
);
795 signal(SIGTSTP
,SIG_DFL
);
797 // Close all of our FDs - just in case
798 for (int K
= 3; K
!= sysconf(_SC_OPEN_MAX
); K
++)
800 if(KeepFDs
.find(K
) == KeepFDs
.end())
801 fcntl(K
,F_SETFD
,FD_CLOEXEC
);
808 // ExecWait - Fancy waitpid /*{{{*/
809 // ---------------------------------------------------------------------
810 /* Waits for the given sub process. If Reap is set then no errors are
811 generated. Otherwise a failed subprocess will generate a proper descriptive
813 bool ExecWait(pid_t Pid
,const char *Name
,bool Reap
)
818 // Wait and collect the error code
820 while (waitpid(Pid
,&Status
,0) != Pid
)
828 return _error
->Error(_("Waited for %s but it wasn't there"),Name
);
832 // Check for an error code.
833 if (WIFEXITED(Status
) == 0 || WEXITSTATUS(Status
) != 0)
837 if (WIFSIGNALED(Status
) != 0)
839 if( WTERMSIG(Status
) == SIGSEGV
)
840 return _error
->Error(_("Sub-process %s received a segmentation fault."),Name
);
842 return _error
->Error(_("Sub-process %s received signal %u."),Name
, WTERMSIG(Status
));
845 if (WIFEXITED(Status
) != 0)
846 return _error
->Error(_("Sub-process %s returned an error code (%u)"),Name
,WEXITSTATUS(Status
));
848 return _error
->Error(_("Sub-process %s exited unexpectedly"),Name
);
856 // StartsWithGPGClearTextSignature - Check if a file is Pgp/GPG clearsigned /*{{{*/
857 // ---------------------------------------------------------------------
859 bool StartsWithGPGClearTextSignature(string
const &FileName
)
861 static const char* SIGMSG
= "-----BEGIN PGP SIGNED MESSAGE-----\n";
862 char buffer
[strlen(SIGMSG
)+1];
863 FILE* gpg
= fopen(FileName
.c_str(), "r");
867 char const * const test
= fgets(buffer
, sizeof(buffer
), gpg
);
869 if (test
== NULL
|| strcmp(buffer
, SIGMSG
) != 0)
876 class FileFdPrivate
{ /*{{{*/
887 uint8_t buffer
[4096];
893 LZMAFILE() : file(NULL
), eof(false), compressing(false) {}
895 if (compressing
== true)
898 stream
.avail_out
= sizeof(buffer
)/sizeof(buffer
[0]);
899 stream
.next_out
= buffer
;
900 err
= lzma_code(&stream
, LZMA_FINISH
);
901 if (err
!= LZMA_OK
&& err
!= LZMA_STREAM_END
)
903 _error
->Error("~LZMAFILE: Compress finalisation failed");
906 size_t const n
= sizeof(buffer
)/sizeof(buffer
[0]) - stream
.avail_out
;
907 if (n
&& fwrite(buffer
, 1, n
, file
) != n
)
909 _error
->Errno("~LZMAFILE",_("Write error"));
912 if (err
== LZMA_STREAM_END
)
923 pid_t compressor_pid
;
925 APT::Configuration::Compressor compressor
;
926 unsigned int openmode
;
927 unsigned long long seekpos
;
938 compressed_fd(-1), compressor_pid(-1), pipe(false),
939 openmode(0), seekpos(0) {};
940 bool InternalClose(std::string
const &FileName
)
943 /* dummy so that the rest can be 'else if's */;
945 else if (gz
!= NULL
) {
946 int const e
= gzclose(gz
);
948 // gzdclose() on empty files always fails with "buffer error" here, ignore that
949 if (e
!= 0 && e
!= Z_BUF_ERROR
)
950 return _error
->Errno("close",_("Problem closing the gzip file %s"), FileName
.c_str());
954 else if (bz2
!= NULL
) {
960 else if (lzma
!= NULL
) {
967 bool CloseDown(std::string
const &FileName
)
969 bool const Res
= InternalClose(FileName
);
971 if (compressor_pid
> 0)
972 ExecWait(compressor_pid
, "FileFdCompressor", true);
977 bool InternalStream() const {
989 ~FileFdPrivate() { CloseDown(""); }
992 // FileFd::Open - Open a file /*{{{*/
993 // ---------------------------------------------------------------------
994 /* The most commonly used open mode combinations are given with Mode */
995 bool FileFd::Open(string FileName
,unsigned int const Mode
,CompressMode Compress
, unsigned long const AccessMode
)
997 if (Mode
== ReadOnlyGzip
)
998 return Open(FileName
, ReadOnly
, Gzip
, AccessMode
);
1000 if (Compress
== Auto
&& (Mode
& WriteOnly
) == WriteOnly
)
1001 return FileFdError("Autodetection on %s only works in ReadOnly openmode!", FileName
.c_str());
1003 std::vector
<APT::Configuration::Compressor
> const compressors
= APT::Configuration::getCompressors();
1004 std::vector
<APT::Configuration::Compressor
>::const_iterator compressor
= compressors
.begin();
1005 if (Compress
== Auto
)
1007 for (; compressor
!= compressors
.end(); ++compressor
)
1009 std::string file
= FileName
+ compressor
->Extension
;
1010 if (FileExists(file
) == false)
1016 else if (Compress
== Extension
)
1018 std::string::size_type
const found
= FileName
.find_last_of('.');
1020 if (found
!= std::string::npos
)
1022 ext
= FileName
.substr(found
);
1023 if (ext
== ".new" || ext
== ".bak")
1025 std::string::size_type
const found2
= FileName
.find_last_of('.', found
- 1);
1026 if (found2
!= std::string::npos
)
1027 ext
= FileName
.substr(found2
, found
- found2
);
1032 for (; compressor
!= compressors
.end(); ++compressor
)
1033 if (ext
== compressor
->Extension
)
1035 // no matching extension - assume uncompressed (imagine files like 'example.org_Packages')
1036 if (compressor
== compressors
.end())
1037 for (compressor
= compressors
.begin(); compressor
!= compressors
.end(); ++compressor
)
1038 if (compressor
->Name
== ".")
1046 case None
: name
= "."; break;
1047 case Gzip
: name
= "gzip"; break;
1048 case Bzip2
: name
= "bzip2"; break;
1049 case Lzma
: name
= "lzma"; break;
1050 case Xz
: name
= "xz"; break;
1054 return FileFdError("Opening File %s in None, Auto or Extension should be already handled?!?", FileName
.c_str());
1056 for (; compressor
!= compressors
.end(); ++compressor
)
1057 if (compressor
->Name
== name
)
1059 if (compressor
== compressors
.end())
1060 return FileFdError("Can't find a configured compressor %s for file %s", name
.c_str(), FileName
.c_str());
1063 if (compressor
== compressors
.end())
1064 return FileFdError("Can't find a match for specified compressor mode for file %s", FileName
.c_str());
1065 return Open(FileName
, Mode
, *compressor
, AccessMode
);
1067 bool FileFd::Open(string FileName
,unsigned int const Mode
,APT::Configuration::Compressor
const &compressor
, unsigned long const AccessMode
)
1072 if ((Mode
& WriteOnly
) != WriteOnly
&& (Mode
& (Atomic
| Create
| Empty
| Exclusive
)) != 0)
1073 return FileFdError("ReadOnly mode for %s doesn't accept additional flags!", FileName
.c_str());
1074 if ((Mode
& ReadWrite
) == 0)
1075 return FileFdError("No openmode provided in FileFd::Open for %s", FileName
.c_str());
1077 if ((Mode
& Atomic
) == Atomic
)
1081 else if ((Mode
& (Exclusive
| Create
)) == (Exclusive
| Create
))
1083 // for atomic, this will be done by rename in Close()
1084 unlink(FileName
.c_str());
1086 if ((Mode
& Empty
) == Empty
)
1089 if (lstat(FileName
.c_str(),&Buf
) == 0 && S_ISLNK(Buf
.st_mode
))
1090 unlink(FileName
.c_str());
1094 #define if_FLAGGED_SET(FLAG, MODE) if ((Mode & FLAG) == FLAG) fileflags |= MODE
1095 if_FLAGGED_SET(ReadWrite
, O_RDWR
);
1096 else if_FLAGGED_SET(ReadOnly
, O_RDONLY
);
1097 else if_FLAGGED_SET(WriteOnly
, O_WRONLY
);
1099 if_FLAGGED_SET(Create
, O_CREAT
);
1100 if_FLAGGED_SET(Empty
, O_TRUNC
);
1101 if_FLAGGED_SET(Exclusive
, O_EXCL
);
1102 #undef if_FLAGGED_SET
1104 if ((Mode
& Atomic
) == Atomic
)
1106 char *name
= strdup((FileName
+ ".XXXXXX").c_str());
1108 if((iFd
= mkstemp(name
)) == -1)
1111 return FileFdErrno("mkstemp", "Could not create temporary file for %s", FileName
.c_str());
1114 TemporaryFileName
= string(name
);
1117 // umask() will always set the umask and return the previous value, so
1118 // we first set the umask and then reset it to the old value
1119 mode_t
const CurrentUmask
= umask(0);
1120 umask(CurrentUmask
);
1121 // calculate the actual file permissions (just like open/creat)
1122 mode_t
const FilePermissions
= (AccessMode
& ~CurrentUmask
);
1124 if(fchmod(iFd
, FilePermissions
) == -1)
1125 return FileFdErrno("fchmod", "Could not change permissions for temporary file %s", TemporaryFileName
.c_str());
1128 iFd
= open(FileName
.c_str(), fileflags
, AccessMode
);
1130 this->FileName
= FileName
;
1131 if (iFd
== -1 || OpenInternDescriptor(Mode
, compressor
) == false)
1138 return FileFdErrno("open",_("Could not open file %s"), FileName
.c_str());
1141 SetCloseExec(iFd
,true);
1145 // FileFd::OpenDescriptor - Open a filedescriptor /*{{{*/
1146 // ---------------------------------------------------------------------
1148 bool FileFd::OpenDescriptor(int Fd
, unsigned int const Mode
, CompressMode Compress
, bool AutoClose
)
1150 std::vector
<APT::Configuration::Compressor
> const compressors
= APT::Configuration::getCompressors();
1151 std::vector
<APT::Configuration::Compressor
>::const_iterator compressor
= compressors
.begin();
1154 // compat with the old API
1155 if (Mode
== ReadOnlyGzip
&& Compress
== None
)
1160 case None
: name
= "."; break;
1161 case Gzip
: name
= "gzip"; break;
1162 case Bzip2
: name
= "bzip2"; break;
1163 case Lzma
: name
= "lzma"; break;
1164 case Xz
: name
= "xz"; break;
1167 if (AutoClose
== true && Fd
!= -1)
1169 return FileFdError("Opening Fd %d in Auto or Extension compression mode is not supported", Fd
);
1171 for (; compressor
!= compressors
.end(); ++compressor
)
1172 if (compressor
->Name
== name
)
1174 if (compressor
== compressors
.end())
1176 if (AutoClose
== true && Fd
!= -1)
1178 return FileFdError("Can't find a configured compressor %s for file %s", name
.c_str(), FileName
.c_str());
1180 return OpenDescriptor(Fd
, Mode
, *compressor
, AutoClose
);
1182 bool FileFd::OpenDescriptor(int Fd
, unsigned int const Mode
, APT::Configuration::Compressor
const &compressor
, bool AutoClose
)
1185 Flags
= (AutoClose
) ? FileFd::AutoClose
: 0;
1187 this->FileName
= "";
1188 if (OpenInternDescriptor(Mode
, compressor
) == false)
1191 (Flags
& Compressed
) == Compressed
||
1197 return FileFdError(_("Could not open file descriptor %d"), Fd
);
1201 bool FileFd::OpenInternDescriptor(unsigned int const Mode
, APT::Configuration::Compressor
const &compressor
)
1205 if (compressor
.Name
== "." || compressor
.Binary
.empty() == true)
1208 #if defined HAVE_ZLIB || defined HAVE_BZ2 || defined HAVE_LZMA
1209 // the API to open files is similar, so setup to avoid code duplicates later
1210 // and while at it ensure that we close before opening (if its a reopen)
1211 void* (*compress_open
)(int, const char *) = NULL
;
1213 /* dummy so that the rest can be 'else if's */;
1214 #define APT_COMPRESS_INIT(NAME,OPEN) \
1215 else if (compressor.Name == NAME) \
1217 compress_open = (void*(*)(int, const char *)) OPEN; \
1218 if (d != NULL) d->InternalClose(FileName); \
1221 APT_COMPRESS_INIT("gzip", gzdopen
)
1224 APT_COMPRESS_INIT("bzip2", BZ2_bzdopen
)
1227 APT_COMPRESS_INIT("xz", fdopen
)
1228 APT_COMPRESS_INIT("lzma", fdopen
)
1230 #undef APT_COMPRESS_INIT
1235 d
= new FileFdPrivate();
1237 d
->compressor
= compressor
;
1238 #if defined HAVE_ZLIB || defined HAVE_BZ2 || defined HAVE_LZMA
1239 if ((Flags
& AutoClose
) != AutoClose
&& compress_open
!= NULL
)
1241 // Need to duplicate fd here or gz/bz2 close for cleanup will close the fd as well
1242 int const internFd
= dup(iFd
);
1244 return FileFdErrno("OpenInternDescriptor", _("Could not open file descriptor %d"), iFd
);
1250 #if defined HAVE_ZLIB || defined HAVE_BZ2 || defined HAVE_LZMA
1251 if (compress_open
!= NULL
)
1253 void* compress_struct
= NULL
;
1254 if ((Mode
& ReadWrite
) == ReadWrite
)
1255 compress_struct
= compress_open(iFd
, "r+");
1256 else if ((Mode
& WriteOnly
) == WriteOnly
)
1257 compress_struct
= compress_open(iFd
, "w");
1259 compress_struct
= compress_open(iFd
, "r");
1260 if (compress_struct
== NULL
)
1264 /* dummy so that the rest can be 'else if's */;
1266 else if (compressor
.Name
== "gzip")
1267 d
->gz
= (gzFile
) compress_struct
;
1270 else if (compressor
.Name
== "bzip2")
1271 d
->bz2
= (BZFILE
*) compress_struct
;
1274 else if (compressor
.Name
== "xz" || compressor
.Name
== "lzma")
1276 uint32_t const xzlevel
= 6;
1277 uint64_t const memlimit
= UINT64_MAX
;
1278 if (d
->lzma
== NULL
)
1279 d
->lzma
= new FileFdPrivate::LZMAFILE
;
1280 d
->lzma
->file
= (FILE*) compress_struct
;
1281 lzma_stream tmp_stream
= LZMA_STREAM_INIT
;
1282 d
->lzma
->stream
= tmp_stream
;
1284 if ((Mode
& ReadWrite
) == ReadWrite
)
1285 return FileFdError("ReadWrite mode is not supported for file %s", FileName
.c_str());
1287 if ((Mode
& WriteOnly
) == WriteOnly
)
1289 if (compressor
.Name
== "xz")
1291 if (lzma_easy_encoder(&d
->lzma
->stream
, xzlevel
, LZMA_CHECK_CRC32
) != LZMA_OK
)
1296 lzma_options_lzma options
;
1297 lzma_lzma_preset(&options
, xzlevel
);
1298 if (lzma_alone_encoder(&d
->lzma
->stream
, &options
) != LZMA_OK
)
1301 d
->lzma
->compressing
= true;
1305 if (compressor
.Name
== "xz")
1307 if (lzma_auto_decoder(&d
->lzma
->stream
, memlimit
, 0) != LZMA_OK
)
1312 if (lzma_alone_decoder(&d
->lzma
->stream
, memlimit
) != LZMA_OK
)
1315 d
->lzma
->compressing
= false;
1319 Flags
|= Compressed
;
1324 // collect zombies here in case we reopen
1325 if (d
->compressor_pid
> 0)
1326 ExecWait(d
->compressor_pid
, "FileFdCompressor", true);
1328 if ((Mode
& ReadWrite
) == ReadWrite
)
1329 return FileFdError("ReadWrite mode is not supported for file %s", FileName
.c_str());
1331 bool const Comp
= (Mode
& WriteOnly
) == WriteOnly
;
1334 // Handle 'decompression' of empty files
1337 if (Buf
.st_size
== 0 && S_ISFIFO(Buf
.st_mode
) == false)
1340 // We don't need the file open - instead let the compressor open it
1341 // as he properly knows better how to efficiently read from 'his' file
1342 if (FileName
.empty() == false)
1349 // Create a data pipe
1350 int Pipe
[2] = {-1,-1};
1351 if (pipe(Pipe
) != 0)
1352 return FileFdErrno("pipe",_("Failed to create subprocess IPC"));
1353 for (int J
= 0; J
!= 2; J
++)
1354 SetCloseExec(Pipe
[J
],true);
1356 d
->compressed_fd
= iFd
;
1365 d
->compressor_pid
= ExecFork();
1366 if (d
->compressor_pid
== 0)
1370 dup2(d
->compressed_fd
,STDOUT_FILENO
);
1371 dup2(Pipe
[0],STDIN_FILENO
);
1375 if (d
->compressed_fd
!= -1)
1376 dup2(d
->compressed_fd
,STDIN_FILENO
);
1377 dup2(Pipe
[1],STDOUT_FILENO
);
1379 int const nullfd
= open("/dev/null", O_WRONLY
);
1382 dup2(nullfd
,STDERR_FILENO
);
1386 SetCloseExec(STDOUT_FILENO
,false);
1387 SetCloseExec(STDIN_FILENO
,false);
1389 std::vector
<char const*> Args
;
1390 Args
.push_back(compressor
.Binary
.c_str());
1391 std::vector
<std::string
> const * const addArgs
=
1392 (Comp
== true) ? &(compressor
.CompressArgs
) : &(compressor
.UncompressArgs
);
1393 for (std::vector
<std::string
>::const_iterator a
= addArgs
->begin();
1394 a
!= addArgs
->end(); ++a
)
1395 Args
.push_back(a
->c_str());
1396 if (Comp
== false && FileName
.empty() == false)
1398 // commands not needing arguments, do not need to be told about using standard output
1399 // in reality, only testcases with tools like cat, rev, rot13, … are able to trigger this
1400 if (compressor
.CompressArgs
.empty() == false && compressor
.UncompressArgs
.empty() == false)
1401 Args
.push_back("--stdout");
1402 if (TemporaryFileName
.empty() == false)
1403 Args
.push_back(TemporaryFileName
.c_str());
1405 Args
.push_back(FileName
.c_str());
1407 Args
.push_back(NULL
);
1409 execvp(Args
[0],(char **)&Args
[0]);
1410 cerr
<< _("Failed to exec compressor ") << Args
[0] << endl
;
1421 // FileFd::~File - Closes the file /*{{{*/
1422 // ---------------------------------------------------------------------
1423 /* If the proper modes are selected then we close the Fd and possibly
1424 unlink the file on error. */
1429 d
->CloseDown(FileName
);
1434 // FileFd::Read - Read a bit of the file /*{{{*/
1435 // ---------------------------------------------------------------------
1436 /* We are careful to handle interruption by a signal while reading
1438 bool FileFd::Read(void *To
,unsigned long long Size
,unsigned long long *Actual
)
1444 *((char *)To
) = '\0';
1448 /* dummy so that the rest can be 'else if's */;
1450 else if (d
!= NULL
&& d
->gz
!= NULL
)
1451 Res
= gzread(d
->gz
,To
,Size
);
1454 else if (d
!= NULL
&& d
->bz2
!= NULL
)
1455 Res
= BZ2_bzread(d
->bz2
,To
,Size
);
1458 else if (d
!= NULL
&& d
->lzma
!= NULL
)
1460 if (d
->lzma
->eof
== true)
1463 d
->lzma
->stream
.next_out
= (uint8_t *) To
;
1464 d
->lzma
->stream
.avail_out
= Size
;
1465 if (d
->lzma
->stream
.avail_in
== 0)
1467 d
->lzma
->stream
.next_in
= d
->lzma
->buffer
;
1468 d
->lzma
->stream
.avail_in
= fread(d
->lzma
->buffer
, 1, sizeof(d
->lzma
->buffer
)/sizeof(d
->lzma
->buffer
[0]), d
->lzma
->file
);
1470 d
->lzma
->err
= lzma_code(&d
->lzma
->stream
, LZMA_RUN
);
1471 if (d
->lzma
->err
== LZMA_STREAM_END
)
1473 d
->lzma
->eof
= true;
1474 Res
= Size
- d
->lzma
->stream
.avail_out
;
1476 else if (d
->lzma
->err
!= LZMA_OK
)
1483 Res
= Size
- d
->lzma
->stream
.avail_out
;
1486 // lzma run was okay, but produced no output…
1494 Res
= read(iFd
,To
,Size
);
1500 // trick the while-loop into running again
1506 /* dummy so that the rest can be 'else if's */;
1508 else if (d
!= NULL
&& d
->gz
!= NULL
)
1511 char const * const errmsg
= gzerror(d
->gz
, &err
);
1513 return FileFdError("gzread: %s (%d: %s)", _("Read error"), err
, errmsg
);
1517 else if (d
!= NULL
&& d
->bz2
!= NULL
)
1520 char const * const errmsg
= BZ2_bzerror(d
->bz2
, &err
);
1521 if (err
!= BZ_IO_ERROR
)
1522 return FileFdError("BZ2_bzread: %s (%d: %s)", _("Read error"), err
, errmsg
);
1526 else if (d
!= NULL
&& d
->lzma
!= NULL
)
1527 return FileFdError("lzma_read: %s (%d)", _("Read error"), d
->lzma
->err
);
1529 return FileFdErrno("read",_("Read error"));
1532 To
= (char *)To
+ Res
;
1539 while (Res
> 0 && Size
> 0);
1551 return FileFdError(_("read, still have %llu to read but none left"), Size
);
1554 // FileFd::ReadLine - Read a complete line from the file /*{{{*/
1555 // ---------------------------------------------------------------------
1556 /* Beware: This method can be quiet slow for big buffers on UNcompressed
1557 files because of the naive implementation! */
1558 char* FileFd::ReadLine(char *To
, unsigned long long const Size
)
1562 if (d
!= NULL
&& d
->gz
!= NULL
)
1563 return gzgets(d
->gz
, To
, Size
);
1566 unsigned long long read
= 0;
1567 while ((Size
- 1) != read
)
1569 unsigned long long done
= 0;
1570 if (Read(To
+ read
, 1, &done
) == false)
1574 if (To
[read
++] == '\n')
1583 // FileFd::Write - Write to the file /*{{{*/
1584 // ---------------------------------------------------------------------
1586 bool FileFd::Write(const void *From
,unsigned long long Size
)
1593 /* dummy so that the rest can be 'else if's */;
1595 else if (d
!= NULL
&& d
->gz
!= NULL
)
1596 Res
= gzwrite(d
->gz
,From
,Size
);
1599 else if (d
!= NULL
&& d
->bz2
!= NULL
)
1600 Res
= BZ2_bzwrite(d
->bz2
,(void*)From
,Size
);
1603 else if (d
!= NULL
&& d
->lzma
!= NULL
)
1605 d
->lzma
->stream
.next_in
= (uint8_t *)From
;
1606 d
->lzma
->stream
.avail_in
= Size
;
1607 d
->lzma
->stream
.next_out
= d
->lzma
->buffer
;
1608 d
->lzma
->stream
.avail_out
= sizeof(d
->lzma
->buffer
)/sizeof(d
->lzma
->buffer
[0]);
1609 d
->lzma
->err
= lzma_code(&d
->lzma
->stream
, LZMA_RUN
);
1610 if (d
->lzma
->err
!= LZMA_OK
)
1612 size_t const n
= sizeof(d
->lzma
->buffer
)/sizeof(d
->lzma
->buffer
[0]) - d
->lzma
->stream
.avail_out
;
1613 size_t const m
= (n
== 0) ? 0 : fwrite(d
->lzma
->buffer
, 1, n
, d
->lzma
->file
);
1617 Res
= Size
- d
->lzma
->stream
.avail_in
;
1621 Res
= write(iFd
,From
,Size
);
1623 if (Res
< 0 && errno
== EINTR
)
1628 /* dummy so that the rest can be 'else if's */;
1630 else if (d
!= NULL
&& d
->gz
!= NULL
)
1633 char const * const errmsg
= gzerror(d
->gz
, &err
);
1635 return FileFdError("gzwrite: %s (%d: %s)", _("Write error"), err
, errmsg
);
1639 else if (d
!= NULL
&& d
->bz2
!= NULL
)
1642 char const * const errmsg
= BZ2_bzerror(d
->bz2
, &err
);
1643 if (err
!= BZ_IO_ERROR
)
1644 return FileFdError("BZ2_bzwrite: %s (%d: %s)", _("Write error"), err
, errmsg
);
1648 else if (d
!= NULL
&& d
->lzma
!= NULL
)
1649 return FileFdErrno("lzma_fwrite", _("Write error"));
1651 return FileFdErrno("write",_("Write error"));
1654 From
= (char const *)From
+ Res
;
1659 while (Res
> 0 && Size
> 0);
1664 return FileFdError(_("write, still have %llu to write but couldn't"), Size
);
1666 bool FileFd::Write(int Fd
, const void *From
, unsigned long long Size
)
1672 Res
= write(Fd
,From
,Size
);
1673 if (Res
< 0 && errno
== EINTR
)
1676 return _error
->Errno("write",_("Write error"));
1678 From
= (char const *)From
+ Res
;
1681 while (Res
> 0 && Size
> 0);
1686 return _error
->Error(_("write, still have %llu to write but couldn't"), Size
);
1689 // FileFd::Seek - Seek in the file /*{{{*/
1690 // ---------------------------------------------------------------------
1692 bool FileFd::Seek(unsigned long long To
)
1696 if (d
!= NULL
&& (d
->pipe
== true || d
->InternalStream() == true))
1698 // Our poor man seeking in pipes is costly, so try to avoid it
1699 unsigned long long seekpos
= Tell();
1702 else if (seekpos
< To
)
1703 return Skip(To
- seekpos
);
1705 if ((d
->openmode
& ReadOnly
) != ReadOnly
)
1706 return FileFdError("Reopen is only implemented for read-only files!");
1707 d
->InternalClose(FileName
);
1711 if (TemporaryFileName
.empty() == false)
1712 iFd
= open(TemporaryFileName
.c_str(), O_RDONLY
);
1713 else if (FileName
.empty() == false)
1714 iFd
= open(FileName
.c_str(), O_RDONLY
);
1717 if (d
->compressed_fd
> 0)
1718 if (lseek(d
->compressed_fd
, 0, SEEK_SET
) != 0)
1719 iFd
= d
->compressed_fd
;
1721 return FileFdError("Reopen is not implemented for pipes opened with FileFd::OpenDescriptor()!");
1724 if (OpenInternDescriptor(d
->openmode
, d
->compressor
) == false)
1725 return FileFdError("Seek on file %s because it couldn't be reopened", FileName
.c_str());
1735 if (d
!= NULL
&& d
->gz
)
1736 res
= gzseek(d
->gz
,To
,SEEK_SET
);
1739 res
= lseek(iFd
,To
,SEEK_SET
);
1740 if (res
!= (off_t
)To
)
1741 return FileFdError("Unable to seek to %llu", To
);
1748 // FileFd::Skip - Seek in the file /*{{{*/
1749 // ---------------------------------------------------------------------
1751 bool FileFd::Skip(unsigned long long Over
)
1753 if (d
!= NULL
&& (d
->pipe
== true || d
->InternalStream() == true))
1758 unsigned long long toread
= std::min((unsigned long long) sizeof(buffer
), Over
);
1759 if (Read(buffer
, toread
) == false)
1760 return FileFdError("Unable to seek ahead %llu",Over
);
1768 if (d
!= NULL
&& d
->gz
!= NULL
)
1769 res
= gzseek(d
->gz
,Over
,SEEK_CUR
);
1772 res
= lseek(iFd
,Over
,SEEK_CUR
);
1774 return FileFdError("Unable to seek ahead %llu",Over
);
1781 // FileFd::Truncate - Truncate the file /*{{{*/
1782 // ---------------------------------------------------------------------
1784 bool FileFd::Truncate(unsigned long long To
)
1786 // truncating /dev/null is always successful - as we get an error otherwise
1787 if (To
== 0 && FileName
== "/dev/null")
1789 #if defined HAVE_ZLIB || defined HAVE_BZ2 || defined HAVE_LZMA
1790 if (d
!= NULL
&& (d
->InternalStream() == true
1795 return FileFdError("Truncating compressed files is not implemented (%s)", FileName
.c_str());
1797 if (ftruncate(iFd
,To
) != 0)
1798 return FileFdError("Unable to truncate to %llu",To
);
1803 // FileFd::Tell - Current seek position /*{{{*/
1804 // ---------------------------------------------------------------------
1806 unsigned long long FileFd::Tell()
1808 // In theory, we could just return seekpos here always instead of
1809 // seeking around, but not all users of FileFd use always Seek() and co
1810 // so d->seekpos isn't always true and we can just use it as a hint if
1811 // we have nothing else, but not always as an authority…
1812 if (d
!= NULL
&& (d
->pipe
== true || d
->InternalStream() == true))
1817 if (d
!= NULL
&& d
->gz
!= NULL
)
1818 Res
= gztell(d
->gz
);
1821 Res
= lseek(iFd
,0,SEEK_CUR
);
1822 if (Res
== (off_t
)-1)
1823 FileFdErrno("lseek","Failed to determine the current file position");
1829 static bool StatFileFd(char const * const msg
, int const iFd
, std::string
const &FileName
, struct stat
&Buf
, FileFdPrivate
* const d
) /*{{{*/
1831 bool ispipe
= (d
!= NULL
&& d
->pipe
== true);
1832 if (ispipe
== false)
1834 if (fstat(iFd
,&Buf
) != 0)
1835 // higher-level code will generate more meaningful messages,
1836 // even translated this would be meaningless for users
1837 return _error
->Errno("fstat", "Unable to determine %s for fd %i", msg
, iFd
);
1838 if (FileName
.empty() == false)
1839 ispipe
= S_ISFIFO(Buf
.st_mode
);
1842 // for compressor pipes st_size is undefined and at 'best' zero
1845 // we set it here, too, as we get the info here for free
1846 // in theory the Open-methods should take care of it already
1849 if (stat(FileName
.c_str(), &Buf
) != 0)
1850 return _error
->Errno("fstat", "Unable to determine %s for file %s", msg
, FileName
.c_str());
1855 // FileFd::FileSize - Return the size of the file /*{{{*/
1856 unsigned long long FileFd::FileSize()
1859 if (StatFileFd("file size", iFd
, FileName
, Buf
, d
) == false)
1867 // FileFd::ModificationTime - Return the time of last touch /*{{{*/
1868 time_t FileFd::ModificationTime()
1871 if (StatFileFd("modification time", iFd
, FileName
, Buf
, d
) == false)
1876 return Buf
.st_mtime
;
1879 // FileFd::Size - Return the size of the content in the file /*{{{*/
1880 // ---------------------------------------------------------------------
1882 unsigned long long FileFd::Size()
1884 unsigned long long size
= FileSize();
1886 // for compressor pipes st_size is undefined and at 'best' zero,
1887 // so we 'read' the content and 'seek' back - see there
1888 if (d
!= NULL
&& (d
->pipe
== true || (d
->InternalStream() == true && size
> 0)))
1890 unsigned long long const oldSeek
= Tell();
1892 unsigned long long read
= 0;
1894 if (Read(ignore
, sizeof(ignore
), &read
) == false)
1904 // only check gzsize if we are actually a gzip file, just checking for
1905 // "gz" is not sufficient as uncompressed files could be opened with
1906 // gzopen in "direct" mode as well
1907 else if (d
!= NULL
&& d
->gz
&& !gzdirect(d
->gz
) && size
> 0)
1909 off_t
const oldPos
= lseek(iFd
,0,SEEK_CUR
);
1910 /* unfortunately zlib.h doesn't provide a gzsize(), so we have to do
1911 * this ourselves; the original (uncompressed) file size is the last 32
1912 * bits of the file */
1913 // FIXME: Size for gz-files is limited by 32bit… no largefile support
1914 if (lseek(iFd
, -4, SEEK_END
) < 0)
1916 FileFdErrno("lseek","Unable to seek to end of gzipped file");
1920 if (read(iFd
, &size
, 4) != 4)
1922 FileFdErrno("read","Unable to read original size of gzipped file");
1925 size
= le32toh(size
);
1927 if (lseek(iFd
, oldPos
, SEEK_SET
) < 0)
1929 FileFdErrno("lseek","Unable to seek in gzipped file");
1940 // FileFd::Close - Close the file if the close flag is set /*{{{*/
1941 // ---------------------------------------------------------------------
1943 bool FileFd::Close()
1949 if ((Flags
& AutoClose
) == AutoClose
)
1951 if ((Flags
& Compressed
) != Compressed
&& iFd
> 0 && close(iFd
) != 0)
1952 Res
&= _error
->Errno("close",_("Problem closing the file %s"), FileName
.c_str());
1955 Res
&= d
->CloseDown(FileName
);
1961 if ((Flags
& Replace
) == Replace
) {
1962 if (rename(TemporaryFileName
.c_str(), FileName
.c_str()) != 0)
1963 Res
&= _error
->Errno("rename",_("Problem renaming the file %s to %s"), TemporaryFileName
.c_str(), FileName
.c_str());
1965 FileName
= TemporaryFileName
; // for the unlink() below.
1966 TemporaryFileName
.clear();
1971 if ((Flags
& Fail
) == Fail
&& (Flags
& DelOnFail
) == DelOnFail
&&
1972 FileName
.empty() == false)
1973 if (unlink(FileName
.c_str()) != 0)
1974 Res
&= _error
->WarningE("unlnk",_("Problem unlinking the file %s"), FileName
.c_str());
1981 // FileFd::Sync - Sync the file /*{{{*/
1982 // ---------------------------------------------------------------------
1986 if (fsync(iFd
) != 0)
1987 return FileFdErrno("sync",_("Problem syncing the file"));
1991 // FileFd::FileFdErrno - set Fail and call _error->Errno *{{{*/
1992 bool FileFd::FileFdErrno(const char *Function
, const char *Description
,...)
1996 size_t msgSize
= 400;
1997 int const errsv
= errno
;
2000 va_start(args
,Description
);
2001 if (_error
->InsertErrno(GlobalError::ERROR
, Function
, Description
, args
, errsv
, msgSize
) == false)
2008 // FileFd::FileFdError - set Fail and call _error->Error *{{{*/
2009 bool FileFd::FileFdError(const char *Description
,...) {
2012 size_t msgSize
= 400;
2015 va_start(args
,Description
);
2016 if (_error
->Insert(GlobalError::ERROR
, Description
, args
, msgSize
) == false)
2024 APT_DEPRECATED gzFile
FileFd::gzFd() {
2033 // Glob - wrapper around "glob()" /*{{{*/
2034 // ---------------------------------------------------------------------
2036 std::vector
<std::string
> Glob(std::string
const &pattern
, int flags
)
2038 std::vector
<std::string
> result
;
2043 glob_res
= glob(pattern
.c_str(), flags
, NULL
, &globbuf
);
2047 if(glob_res
!= GLOB_NOMATCH
) {
2048 _error
->Errno("glob", "Problem with glob");
2054 for(i
=0;i
<globbuf
.gl_pathc
;i
++)
2055 result
.push_back(string(globbuf
.gl_pathv
[i
]));
2062 std::string
GetTempDir()
2064 const char *tmpdir
= getenv("TMPDIR");
2071 // check that tmpdir is set and exists
2073 if (!tmpdir
|| strlen(tmpdir
) == 0 || stat(tmpdir
, &st
) != 0)
2076 return string(tmpdir
);
2079 FileFd
* GetTempFile(std::string
const &Prefix
, bool ImmediateUnlink
)
2082 FileFd
*Fd
= new FileFd();
2084 std::string tempdir
= GetTempDir();
2085 snprintf(fn
, sizeof(fn
), "%s/%s.XXXXXX",
2086 tempdir
.c_str(), Prefix
.c_str());
2087 int fd
= mkstemp(fn
);
2092 _error
->Errno("GetTempFile",_("Unable to mkstemp %s"), fn
);
2095 if (!Fd
->OpenDescriptor(fd
, FileFd::WriteOnly
, FileFd::None
, true))
2097 _error
->Errno("GetTempFile",_("Unable to write to %s"),fn
);
2104 bool Rename(std::string From
, std::string To
)
2106 if (rename(From
.c_str(),To
.c_str()) != 0)
2108 _error
->Error(_("rename failed, %s (%s -> %s)."),strerror(errno
),
2109 From
.c_str(),To
.c_str());
2115 bool Popen(const char* Args
[], FileFd
&Fd
, pid_t
&Child
, FileFd::OpenMode Mode
)
2118 if (Mode
!= FileFd::ReadOnly
&& Mode
!= FileFd::WriteOnly
)
2119 return _error
->Error("Popen supports ReadOnly (x)or WriteOnly mode only");
2121 int Pipe
[2] = {-1, -1};
2124 return _error
->Errno("pipe", _("Failed to create subprocess IPC"));
2127 std::set
<int> keep_fds
;
2128 keep_fds
.insert(Pipe
[0]);
2129 keep_fds
.insert(Pipe
[1]);
2130 Child
= ExecFork(keep_fds
);
2132 return _error
->Errno("fork", "Failed to fork");
2135 if(Mode
== FileFd::ReadOnly
)
2140 else if(Mode
== FileFd::WriteOnly
)
2146 if(Mode
== FileFd::ReadOnly
)
2150 } else if(Mode
== FileFd::WriteOnly
)
2153 execv(Args
[0], (char**)Args
);
2156 if(Mode
== FileFd::ReadOnly
)
2160 } else if(Mode
== FileFd::WriteOnly
)
2165 Fd
.OpenDescriptor(fd
, Mode
, FileFd::None
, true);