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>
72 // RunScripts - Run a set of scripts from a configuration subtree /*{{{*/
73 // ---------------------------------------------------------------------
75 bool RunScripts(const char *Cnf
)
77 Configuration::Item
const *Opts
= _config
->Tree(Cnf
);
78 if (Opts
== 0 || Opts
->Child
== 0)
82 // Fork for running the system calls
83 pid_t Child
= ExecFork();
88 if (_config
->FindDir("DPkg::Chroot-Directory","/") != "/")
90 std::cerr
<< "Chrooting into "
91 << _config
->FindDir("DPkg::Chroot-Directory")
93 if (chroot(_config
->FindDir("DPkg::Chroot-Directory","/").c_str()) != 0)
97 if (chdir("/tmp/") != 0)
100 unsigned int Count
= 1;
101 for (; Opts
!= 0; Opts
= Opts
->Next
, Count
++)
103 if (Opts
->Value
.empty() == true)
106 if(_config
->FindB("Debug::RunScripts", false) == true)
107 std::clog
<< "Running external script: '"
108 << Opts
->Value
<< "'" << std::endl
;
110 if (system(Opts
->Value
.c_str()) != 0)
116 // Wait for the child
118 while (waitpid(Child
,&Status
,0) != Child
)
122 return _error
->Errno("waitpid","Couldn't wait for subprocess");
125 // Restore sig int/quit
126 signal(SIGQUIT
,SIG_DFL
);
127 signal(SIGINT
,SIG_DFL
);
129 // Check for an error code.
130 if (WIFEXITED(Status
) == 0 || WEXITSTATUS(Status
) != 0)
132 unsigned int Count
= WEXITSTATUS(Status
);
136 for (; Opts
!= 0 && Count
!= 1; Opts
= Opts
->Next
, Count
--);
137 _error
->Error("Problem executing scripts %s '%s'",Cnf
,Opts
->Value
.c_str());
140 return _error
->Error("Sub-process returned an error code");
147 // CopyFile - Buffered copy of a file /*{{{*/
148 // ---------------------------------------------------------------------
149 /* The caller is expected to set things so that failure causes erasure */
150 bool CopyFile(FileFd
&From
,FileFd
&To
)
152 if (From
.IsOpen() == false || To
.IsOpen() == false ||
153 From
.Failed() == true || To
.Failed() == true)
156 // Buffered copy between fds
157 SPtrArray
<unsigned char> Buf
= new unsigned char[64000];
158 unsigned long long Size
= From
.Size();
161 unsigned long long ToRead
= Size
;
165 if (From
.Read(Buf
,ToRead
) == false ||
166 To
.Write(Buf
,ToRead
) == false)
175 // GetLock - Gets a lock file /*{{{*/
176 // ---------------------------------------------------------------------
177 /* This will create an empty file of the given name and lock it. Once this
178 is done all other calls to GetLock in any other process will fail with
179 -1. The return result is the fd of the file, the call should call
180 close at some time. */
181 int GetLock(string File
,bool Errors
)
183 // GetLock() is used in aptitude on directories with public-write access
184 // Use O_NOFOLLOW here to prevent symlink traversal attacks
185 int FD
= open(File
.c_str(),O_RDWR
| O_CREAT
| O_NOFOLLOW
,0640);
188 // Read only .. can't have locking problems there.
191 _error
->Warning(_("Not using locking for read only lock file %s"),File
.c_str());
192 return dup(0); // Need something for the caller to close
196 _error
->Errno("open",_("Could not open lock file %s"),File
.c_str());
198 // Feh.. We do this to distinguish the lock vs open case..
202 SetCloseExec(FD
,true);
204 // Acquire a write lock
207 fl
.l_whence
= SEEK_SET
;
210 if (fcntl(FD
,F_SETLK
,&fl
) == -1)
212 // always close to not leak resources
219 _error
->Warning(_("Not using locking for nfs mounted lock file %s"),File
.c_str());
220 return dup(0); // Need something for the caller to close
224 _error
->Errno("open",_("Could not get lock %s"),File
.c_str());
232 // FileExists - Check if a file exists /*{{{*/
233 // ---------------------------------------------------------------------
234 /* Beware: Directories are also files! */
235 bool FileExists(string File
)
238 if (stat(File
.c_str(),&Buf
) != 0)
243 // RealFileExists - Check if a file exists and if it is really a file /*{{{*/
244 // ---------------------------------------------------------------------
246 bool RealFileExists(string File
)
249 if (stat(File
.c_str(),&Buf
) != 0)
251 return ((Buf
.st_mode
& S_IFREG
) != 0);
254 // DirectoryExists - Check if a directory exists and is really one /*{{{*/
255 // ---------------------------------------------------------------------
257 bool DirectoryExists(string
const &Path
)
260 if (stat(Path
.c_str(),&Buf
) != 0)
262 return ((Buf
.st_mode
& S_IFDIR
) != 0);
265 // CreateDirectory - poor man's mkdir -p guarded by a parent directory /*{{{*/
266 // ---------------------------------------------------------------------
267 /* This method will create all directories needed for path in good old
268 mkdir -p style but refuses to do this if Parent is not a prefix of
269 this Path. Example: /var/cache/ and /var/cache/apt/archives are given,
270 so it will create apt/archives if /var/cache exists - on the other
271 hand if the parent is /var/lib the creation will fail as this path
272 is not a parent of the path to be generated. */
273 bool CreateDirectory(string
const &Parent
, string
const &Path
)
275 if (Parent
.empty() == true || Path
.empty() == true)
278 if (DirectoryExists(Path
) == true)
281 if (DirectoryExists(Parent
) == false)
284 // we are not going to create directories "into the blue"
285 if (Path
.compare(0, Parent
.length(), Parent
) != 0)
288 vector
<string
> const dirs
= VectorizeString(Path
.substr(Parent
.size()), '/');
289 string progress
= Parent
;
290 for (vector
<string
>::const_iterator d
= dirs
.begin(); d
!= dirs
.end(); ++d
)
292 if (d
->empty() == true)
295 progress
.append("/").append(*d
);
296 if (DirectoryExists(progress
) == true)
299 if (mkdir(progress
.c_str(), 0755) != 0)
305 // CreateAPTDirectoryIfNeeded - ensure that the given directory exists /*{{{*/
306 // ---------------------------------------------------------------------
307 /* a small wrapper around CreateDirectory to check if it exists and to
308 remove the trailing "/apt/" from the parent directory if needed */
309 bool CreateAPTDirectoryIfNeeded(string
const &Parent
, string
const &Path
)
311 if (DirectoryExists(Path
) == true)
314 size_t const len
= Parent
.size();
315 if (len
> 5 && Parent
.find("/apt/", len
- 6, 5) == len
- 5)
317 if (CreateDirectory(Parent
.substr(0,len
-5), Path
) == true)
320 else if (CreateDirectory(Parent
, Path
) == true)
326 // GetListOfFilesInDir - returns a vector of files in the given dir /*{{{*/
327 // ---------------------------------------------------------------------
328 /* If an extension is given only files with this extension are included
329 in the returned vector, otherwise every "normal" file is included. */
330 std::vector
<string
> GetListOfFilesInDir(string
const &Dir
, string
const &Ext
,
331 bool const &SortList
, bool const &AllowNoExt
)
333 std::vector
<string
> ext
;
335 if (Ext
.empty() == false)
337 if (AllowNoExt
== true && ext
.empty() == false)
339 return GetListOfFilesInDir(Dir
, ext
, SortList
);
341 std::vector
<string
> GetListOfFilesInDir(string
const &Dir
, std::vector
<string
> const &Ext
,
342 bool const &SortList
)
344 // Attention debuggers: need to be set with the environment config file!
345 bool const Debug
= _config
->FindB("Debug::GetListOfFilesInDir", false);
348 std::clog
<< "Accept in " << Dir
<< " only files with the following " << Ext
.size() << " extensions:" << std::endl
;
349 if (Ext
.empty() == true)
350 std::clog
<< "\tNO extension" << std::endl
;
352 for (std::vector
<string
>::const_iterator e
= Ext
.begin();
354 std::clog
<< '\t' << (e
->empty() == true ? "NO" : *e
) << " extension" << std::endl
;
357 std::vector
<string
> List
;
359 if (DirectoryExists(Dir
) == false)
361 _error
->Error(_("List of files can't be created as '%s' is not a directory"), Dir
.c_str());
365 Configuration::MatchAgainstConfig
SilentIgnore("Dir::Ignore-Files-Silently");
366 DIR *D
= opendir(Dir
.c_str());
369 _error
->Errno("opendir",_("Unable to read %s"),Dir
.c_str());
373 for (struct dirent
*Ent
= readdir(D
); Ent
!= 0; Ent
= readdir(D
))
375 // skip "hidden" files
376 if (Ent
->d_name
[0] == '.')
379 // Make sure it is a file and not something else
380 string
const File
= flCombine(Dir
,Ent
->d_name
);
381 #ifdef _DIRENT_HAVE_D_TYPE
382 if (Ent
->d_type
!= DT_REG
)
385 if (RealFileExists(File
) == false)
387 // do not show ignoration warnings for directories
389 #ifdef _DIRENT_HAVE_D_TYPE
390 Ent
->d_type
== DT_DIR
||
392 DirectoryExists(File
) == true)
394 if (SilentIgnore
.Match(Ent
->d_name
) == false)
395 _error
->Notice(_("Ignoring '%s' in directory '%s' as it is not a regular file"), Ent
->d_name
, Dir
.c_str());
400 // check for accepted extension:
401 // no extension given -> periods are bad as hell!
402 // extensions given -> "" extension allows no extension
403 if (Ext
.empty() == false)
405 string d_ext
= flExtension(Ent
->d_name
);
406 if (d_ext
== Ent
->d_name
) // no extension
408 if (std::find(Ext
.begin(), Ext
.end(), "") == Ext
.end())
411 std::clog
<< "Bad file: " << Ent
->d_name
<< " → no extension" << std::endl
;
412 if (SilentIgnore
.Match(Ent
->d_name
) == false)
413 _error
->Notice(_("Ignoring file '%s' in directory '%s' as it has no filename extension"), Ent
->d_name
, Dir
.c_str());
417 else if (std::find(Ext
.begin(), Ext
.end(), d_ext
) == Ext
.end())
420 std::clog
<< "Bad file: " << Ent
->d_name
<< " → bad extension »" << flExtension(Ent
->d_name
) << "«" << std::endl
;
421 if (SilentIgnore
.Match(Ent
->d_name
) == false)
422 _error
->Notice(_("Ignoring file '%s' in directory '%s' as it has an invalid filename extension"), Ent
->d_name
, Dir
.c_str());
427 // Skip bad filenames ala run-parts
428 const char *C
= Ent
->d_name
;
430 if (isalpha(*C
) == 0 && isdigit(*C
) == 0
431 && *C
!= '_' && *C
!= '-' && *C
!= ':') {
432 // no required extension -> dot is a bad character
433 if (*C
== '.' && Ext
.empty() == false)
438 // we don't reach the end of the name -> bad character included
442 std::clog
<< "Bad file: " << Ent
->d_name
<< " → bad character »"
443 << *C
<< "« in filename (period allowed: " << (Ext
.empty() ? "no" : "yes") << ")" << std::endl
;
447 // skip filenames which end with a period. These are never valid
451 std::clog
<< "Bad file: " << Ent
->d_name
<< " → Period as last character" << std::endl
;
456 std::clog
<< "Accept file: " << Ent
->d_name
<< " in " << Dir
<< std::endl
;
457 List
.push_back(File
);
461 if (SortList
== true)
462 std::sort(List
.begin(),List
.end());
465 std::vector
<string
> GetListOfFilesInDir(string
const &Dir
, bool SortList
)
467 bool const Debug
= _config
->FindB("Debug::GetListOfFilesInDir", false);
469 std::clog
<< "Accept in " << Dir
<< " all regular files" << std::endl
;
471 std::vector
<string
> List
;
473 if (DirectoryExists(Dir
) == false)
475 _error
->Error(_("List of files can't be created as '%s' is not a directory"), Dir
.c_str());
479 DIR *D
= opendir(Dir
.c_str());
482 _error
->Errno("opendir",_("Unable to read %s"),Dir
.c_str());
486 for (struct dirent
*Ent
= readdir(D
); Ent
!= 0; Ent
= readdir(D
))
488 // skip "hidden" files
489 if (Ent
->d_name
[0] == '.')
492 // Make sure it is a file and not something else
493 string
const File
= flCombine(Dir
,Ent
->d_name
);
494 #ifdef _DIRENT_HAVE_D_TYPE
495 if (Ent
->d_type
!= DT_REG
)
498 if (RealFileExists(File
) == false)
501 std::clog
<< "Bad file: " << Ent
->d_name
<< " → it is not a real file" << std::endl
;
506 // Skip bad filenames ala run-parts
507 const char *C
= Ent
->d_name
;
509 if (isalpha(*C
) == 0 && isdigit(*C
) == 0
510 && *C
!= '_' && *C
!= '-' && *C
!= '.')
513 // we don't reach the end of the name -> bad character included
517 std::clog
<< "Bad file: " << Ent
->d_name
<< " → bad character »" << *C
<< "« in filename" << std::endl
;
521 // skip filenames which end with a period. These are never valid
525 std::clog
<< "Bad file: " << Ent
->d_name
<< " → Period as last character" << std::endl
;
530 std::clog
<< "Accept file: " << Ent
->d_name
<< " in " << Dir
<< std::endl
;
531 List
.push_back(File
);
535 if (SortList
== true)
536 std::sort(List
.begin(),List
.end());
540 // SafeGetCWD - This is a safer getcwd that returns a dynamic string /*{{{*/
541 // ---------------------------------------------------------------------
542 /* We return / on failure. */
545 // Stash the current dir.
548 if (getcwd(S
,sizeof(S
)-2) == 0)
550 unsigned int Len
= strlen(S
);
556 // GetModificationTime - Get the mtime of the given file or -1 on error /*{{{*/
557 // ---------------------------------------------------------------------
558 /* We return / on failure. */
559 time_t GetModificationTime(string
const &Path
)
562 if (stat(Path
.c_str(), &St
) < 0)
567 // flNotDir - Strip the directory from the filename /*{{{*/
568 // ---------------------------------------------------------------------
570 string
flNotDir(string File
)
572 string::size_type Res
= File
.rfind('/');
573 if (Res
== string::npos
)
576 return string(File
,Res
,Res
- File
.length());
579 // flNotFile - Strip the file from the directory name /*{{{*/
580 // ---------------------------------------------------------------------
581 /* Result ends in a / */
582 string
flNotFile(string File
)
584 string::size_type Res
= File
.rfind('/');
585 if (Res
== string::npos
)
588 return string(File
,0,Res
);
591 // flExtension - Return the extension for the file /*{{{*/
592 // ---------------------------------------------------------------------
594 string
flExtension(string File
)
596 string::size_type Res
= File
.rfind('.');
597 if (Res
== string::npos
)
600 return string(File
,Res
,Res
- File
.length());
603 // flNoLink - If file is a symlink then deref it /*{{{*/
604 // ---------------------------------------------------------------------
605 /* If the name is not a link then the returned path is the input. */
606 string
flNoLink(string File
)
609 if (lstat(File
.c_str(),&St
) != 0 || S_ISLNK(St
.st_mode
) == 0)
611 if (stat(File
.c_str(),&St
) != 0)
614 /* Loop resolving the link. There is no need to limit the number of
615 loops because the stat call above ensures that the symlink is not
623 if ((Res
= readlink(NFile
.c_str(),Buffer
,sizeof(Buffer
))) <= 0 ||
624 (size_t)Res
>= sizeof(Buffer
))
627 // Append or replace the previous path
629 if (Buffer
[0] == '/')
632 NFile
= flNotFile(NFile
) + Buffer
;
634 // See if we are done
635 if (lstat(NFile
.c_str(),&St
) != 0)
637 if (S_ISLNK(St
.st_mode
) == 0)
642 // flCombine - Combine a file and a directory /*{{{*/
643 // ---------------------------------------------------------------------
644 /* If the file is an absolute path then it is just returned, otherwise
645 the directory is pre-pended to it. */
646 string
flCombine(string Dir
,string File
)
648 if (File
.empty() == true)
651 if (File
[0] == '/' || Dir
.empty() == true)
653 if (File
.length() >= 2 && File
[0] == '.' && File
[1] == '/')
655 if (Dir
[Dir
.length()-1] == '/')
657 return Dir
+ '/' + File
;
660 // flAbsPath - Return the absolute path of the filename /*{{{*/
661 // ---------------------------------------------------------------------
663 string
flAbsPath(string File
)
665 char *p
= realpath(File
.c_str(), NULL
);
668 _error
->Errno("realpath", "flAbsPath failed");
671 std::string
AbsPath(p
);
676 // SetCloseExec - Set the close on exec flag /*{{{*/
677 // ---------------------------------------------------------------------
679 void SetCloseExec(int Fd
,bool Close
)
681 if (fcntl(Fd
,F_SETFD
,(Close
== false)?0:FD_CLOEXEC
) != 0)
683 cerr
<< "FATAL -> Could not set close on exec " << strerror(errno
) << endl
;
688 // SetNonBlock - Set the nonblocking flag /*{{{*/
689 // ---------------------------------------------------------------------
691 void SetNonBlock(int Fd
,bool Block
)
693 int Flags
= fcntl(Fd
,F_GETFL
) & (~O_NONBLOCK
);
694 if (fcntl(Fd
,F_SETFL
,Flags
| ((Block
== false)?0:O_NONBLOCK
)) != 0)
696 cerr
<< "FATAL -> Could not set non-blocking flag " << strerror(errno
) << endl
;
701 // WaitFd - Wait for a FD to become readable /*{{{*/
702 // ---------------------------------------------------------------------
703 /* This waits for a FD to become readable using select. It is useful for
704 applications making use of non-blocking sockets. The timeout is
706 bool WaitFd(int Fd
,bool write
,unsigned long timeout
)
719 Res
= select(Fd
+1,0,&Set
,0,(timeout
!= 0?&tv
:0));
721 while (Res
< 0 && errno
== EINTR
);
731 Res
= select(Fd
+1,&Set
,0,0,(timeout
!= 0?&tv
:0));
733 while (Res
< 0 && errno
== EINTR
);
742 // MergeKeepFdsFromConfiguration - Merge APT::Keep-Fds configuration /*{{{*/
743 // ---------------------------------------------------------------------
744 /* This is used to merge the APT::Keep-Fds with the provided KeepFDs
747 void MergeKeepFdsFromConfiguration(std::set
<int> &KeepFDs
)
749 Configuration::Item
const *Opts
= _config
->Tree("APT::Keep-Fds");
750 if (Opts
!= 0 && Opts
->Child
!= 0)
753 for (; Opts
!= 0; Opts
= Opts
->Next
)
755 if (Opts
->Value
.empty() == true)
757 int fd
= atoi(Opts
->Value
.c_str());
763 // ExecFork - Magical fork that sanitizes the context before execing /*{{{*/
764 // ---------------------------------------------------------------------
765 /* This is used if you want to cleanse the environment for the forked
766 child, it fixes up the important signals and nukes all of the fds,
767 otherwise acts like normal fork. */
771 // we need to merge the Keep-Fds as external tools like
772 // debconf-apt-progress use it
773 MergeKeepFdsFromConfiguration(KeepFDs
);
774 return ExecFork(KeepFDs
);
777 pid_t
ExecFork(std::set
<int> KeepFDs
)
779 // Fork off the process
780 pid_t Process
= fork();
783 cerr
<< "FATAL -> Failed to fork." << endl
;
787 // Spawn the subprocess
791 signal(SIGPIPE
,SIG_DFL
);
792 signal(SIGQUIT
,SIG_DFL
);
793 signal(SIGINT
,SIG_DFL
);
794 signal(SIGWINCH
,SIG_DFL
);
795 signal(SIGCONT
,SIG_DFL
);
796 signal(SIGTSTP
,SIG_DFL
);
798 // Close all of our FDs - just in case
799 for (int K
= 3; K
!= sysconf(_SC_OPEN_MAX
); K
++)
801 if(KeepFDs
.find(K
) == KeepFDs
.end())
802 fcntl(K
,F_SETFD
,FD_CLOEXEC
);
809 // ExecWait - Fancy waitpid /*{{{*/
810 // ---------------------------------------------------------------------
811 /* Waits for the given sub process. If Reap is set then no errors are
812 generated. Otherwise a failed subprocess will generate a proper descriptive
814 bool ExecWait(pid_t Pid
,const char *Name
,bool Reap
)
819 // Wait and collect the error code
821 while (waitpid(Pid
,&Status
,0) != Pid
)
829 return _error
->Error(_("Waited for %s but it wasn't there"),Name
);
833 // Check for an error code.
834 if (WIFEXITED(Status
) == 0 || WEXITSTATUS(Status
) != 0)
838 if (WIFSIGNALED(Status
) != 0)
840 if( WTERMSIG(Status
) == SIGSEGV
)
841 return _error
->Error(_("Sub-process %s received a segmentation fault."),Name
);
843 return _error
->Error(_("Sub-process %s received signal %u."),Name
, WTERMSIG(Status
));
846 if (WIFEXITED(Status
) != 0)
847 return _error
->Error(_("Sub-process %s returned an error code (%u)"),Name
,WEXITSTATUS(Status
));
849 return _error
->Error(_("Sub-process %s exited unexpectedly"),Name
);
857 // StartsWithGPGClearTextSignature - Check if a file is Pgp/GPG clearsigned /*{{{*/
858 // ---------------------------------------------------------------------
860 bool StartsWithGPGClearTextSignature(string
const &FileName
)
862 static const char* SIGMSG
= "-----BEGIN PGP SIGNED MESSAGE-----\n";
863 char buffer
[strlen(SIGMSG
)+1];
864 FILE* gpg
= fopen(FileName
.c_str(), "r");
868 char const * const test
= fgets(buffer
, sizeof(buffer
), gpg
);
870 if (test
== NULL
|| strcmp(buffer
, SIGMSG
) != 0)
877 class FileFdPrivate
{ /*{{{*/
888 uint8_t buffer
[4096];
894 LZMAFILE() : file(NULL
), eof(false), compressing(false) {}
896 if (compressing
== true)
899 stream
.avail_out
= sizeof(buffer
)/sizeof(buffer
[0]);
900 stream
.next_out
= buffer
;
901 err
= lzma_code(&stream
, LZMA_FINISH
);
902 if (err
!= LZMA_OK
&& err
!= LZMA_STREAM_END
)
904 _error
->Error("~LZMAFILE: Compress finalisation failed");
907 size_t const n
= sizeof(buffer
)/sizeof(buffer
[0]) - stream
.avail_out
;
908 if (n
&& fwrite(buffer
, 1, n
, file
) != n
)
910 _error
->Errno("~LZMAFILE",_("Write error"));
913 if (err
== LZMA_STREAM_END
)
924 pid_t compressor_pid
;
926 APT::Configuration::Compressor compressor
;
927 unsigned int openmode
;
928 unsigned long long seekpos
;
939 compressed_fd(-1), compressor_pid(-1), pipe(false),
940 openmode(0), seekpos(0) {};
941 bool InternalClose(std::string
const &FileName
)
944 /* dummy so that the rest can be 'else if's */;
946 else if (gz
!= NULL
) {
947 int const e
= gzclose(gz
);
949 // gzdclose() on empty files always fails with "buffer error" here, ignore that
950 if (e
!= 0 && e
!= Z_BUF_ERROR
)
951 return _error
->Errno("close",_("Problem closing the gzip file %s"), FileName
.c_str());
955 else if (bz2
!= NULL
) {
961 else if (lzma
!= NULL
) {
968 bool CloseDown(std::string
const &FileName
)
970 bool const Res
= InternalClose(FileName
);
972 if (compressor_pid
> 0)
973 ExecWait(compressor_pid
, "FileFdCompressor", true);
978 bool InternalStream() const {
990 ~FileFdPrivate() { CloseDown(""); }
993 // FileFd::Open - Open a file /*{{{*/
994 // ---------------------------------------------------------------------
995 /* The most commonly used open mode combinations are given with Mode */
996 bool FileFd::Open(string FileName
,unsigned int const Mode
,CompressMode Compress
, unsigned long const AccessMode
)
998 if (Mode
== ReadOnlyGzip
)
999 return Open(FileName
, ReadOnly
, Gzip
, AccessMode
);
1001 if (Compress
== Auto
&& (Mode
& WriteOnly
) == WriteOnly
)
1002 return FileFdError("Autodetection on %s only works in ReadOnly openmode!", FileName
.c_str());
1004 std::vector
<APT::Configuration::Compressor
> const compressors
= APT::Configuration::getCompressors();
1005 std::vector
<APT::Configuration::Compressor
>::const_iterator compressor
= compressors
.begin();
1006 if (Compress
== Auto
)
1008 for (; compressor
!= compressors
.end(); ++compressor
)
1010 std::string file
= FileName
+ compressor
->Extension
;
1011 if (FileExists(file
) == false)
1017 else if (Compress
== Extension
)
1019 std::string::size_type
const found
= FileName
.find_last_of('.');
1021 if (found
!= std::string::npos
)
1023 ext
= FileName
.substr(found
);
1024 if (ext
== ".new" || ext
== ".bak")
1026 std::string::size_type
const found2
= FileName
.find_last_of('.', found
- 1);
1027 if (found2
!= std::string::npos
)
1028 ext
= FileName
.substr(found2
, found
- found2
);
1033 for (; compressor
!= compressors
.end(); ++compressor
)
1034 if (ext
== compressor
->Extension
)
1036 // no matching extension - assume uncompressed (imagine files like 'example.org_Packages')
1037 if (compressor
== compressors
.end())
1038 for (compressor
= compressors
.begin(); compressor
!= compressors
.end(); ++compressor
)
1039 if (compressor
->Name
== ".")
1047 case None
: name
= "."; break;
1048 case Gzip
: name
= "gzip"; break;
1049 case Bzip2
: name
= "bzip2"; break;
1050 case Lzma
: name
= "lzma"; break;
1051 case Xz
: name
= "xz"; break;
1055 return FileFdError("Opening File %s in None, Auto or Extension should be already handled?!?", FileName
.c_str());
1057 for (; compressor
!= compressors
.end(); ++compressor
)
1058 if (compressor
->Name
== name
)
1060 if (compressor
== compressors
.end())
1061 return FileFdError("Can't find a configured compressor %s for file %s", name
.c_str(), FileName
.c_str());
1064 if (compressor
== compressors
.end())
1065 return FileFdError("Can't find a match for specified compressor mode for file %s", FileName
.c_str());
1066 return Open(FileName
, Mode
, *compressor
, AccessMode
);
1068 bool FileFd::Open(string FileName
,unsigned int const Mode
,APT::Configuration::Compressor
const &compressor
, unsigned long const AccessMode
)
1073 if ((Mode
& WriteOnly
) != WriteOnly
&& (Mode
& (Atomic
| Create
| Empty
| Exclusive
)) != 0)
1074 return FileFdError("ReadOnly mode for %s doesn't accept additional flags!", FileName
.c_str());
1075 if ((Mode
& ReadWrite
) == 0)
1076 return FileFdError("No openmode provided in FileFd::Open for %s", FileName
.c_str());
1078 if ((Mode
& Atomic
) == Atomic
)
1082 else if ((Mode
& (Exclusive
| Create
)) == (Exclusive
| Create
))
1084 // for atomic, this will be done by rename in Close()
1085 unlink(FileName
.c_str());
1087 if ((Mode
& Empty
) == Empty
)
1090 if (lstat(FileName
.c_str(),&Buf
) == 0 && S_ISLNK(Buf
.st_mode
))
1091 unlink(FileName
.c_str());
1095 #define if_FLAGGED_SET(FLAG, MODE) if ((Mode & FLAG) == FLAG) fileflags |= MODE
1096 if_FLAGGED_SET(ReadWrite
, O_RDWR
);
1097 else if_FLAGGED_SET(ReadOnly
, O_RDONLY
);
1098 else if_FLAGGED_SET(WriteOnly
, O_WRONLY
);
1100 if_FLAGGED_SET(Create
, O_CREAT
);
1101 if_FLAGGED_SET(Empty
, O_TRUNC
);
1102 if_FLAGGED_SET(Exclusive
, O_EXCL
);
1103 #undef if_FLAGGED_SET
1105 if ((Mode
& Atomic
) == Atomic
)
1107 char *name
= strdup((FileName
+ ".XXXXXX").c_str());
1109 if((iFd
= mkstemp(name
)) == -1)
1112 return FileFdErrno("mkstemp", "Could not create temporary file for %s", FileName
.c_str());
1115 TemporaryFileName
= string(name
);
1118 // umask() will always set the umask and return the previous value, so
1119 // we first set the umask and then reset it to the old value
1120 mode_t
const CurrentUmask
= umask(0);
1121 umask(CurrentUmask
);
1122 // calculate the actual file permissions (just like open/creat)
1123 mode_t
const FilePermissions
= (AccessMode
& ~CurrentUmask
);
1125 if(fchmod(iFd
, FilePermissions
) == -1)
1126 return FileFdErrno("fchmod", "Could not change permissions for temporary file %s", TemporaryFileName
.c_str());
1129 iFd
= open(FileName
.c_str(), fileflags
, AccessMode
);
1131 this->FileName
= FileName
;
1132 if (iFd
== -1 || OpenInternDescriptor(Mode
, compressor
) == false)
1139 return FileFdErrno("open",_("Could not open file %s"), FileName
.c_str());
1142 SetCloseExec(iFd
,true);
1146 // FileFd::OpenDescriptor - Open a filedescriptor /*{{{*/
1147 // ---------------------------------------------------------------------
1149 bool FileFd::OpenDescriptor(int Fd
, unsigned int const Mode
, CompressMode Compress
, bool AutoClose
)
1151 std::vector
<APT::Configuration::Compressor
> const compressors
= APT::Configuration::getCompressors();
1152 std::vector
<APT::Configuration::Compressor
>::const_iterator compressor
= compressors
.begin();
1155 // compat with the old API
1156 if (Mode
== ReadOnlyGzip
&& Compress
== None
)
1161 case None
: name
= "."; break;
1162 case Gzip
: name
= "gzip"; break;
1163 case Bzip2
: name
= "bzip2"; break;
1164 case Lzma
: name
= "lzma"; break;
1165 case Xz
: name
= "xz"; break;
1168 if (AutoClose
== true && Fd
!= -1)
1170 return FileFdError("Opening Fd %d in Auto or Extension compression mode is not supported", Fd
);
1172 for (; compressor
!= compressors
.end(); ++compressor
)
1173 if (compressor
->Name
== name
)
1175 if (compressor
== compressors
.end())
1177 if (AutoClose
== true && Fd
!= -1)
1179 return FileFdError("Can't find a configured compressor %s for file %s", name
.c_str(), FileName
.c_str());
1181 return OpenDescriptor(Fd
, Mode
, *compressor
, AutoClose
);
1183 bool FileFd::OpenDescriptor(int Fd
, unsigned int const Mode
, APT::Configuration::Compressor
const &compressor
, bool AutoClose
)
1186 Flags
= (AutoClose
) ? FileFd::AutoClose
: 0;
1188 this->FileName
= "";
1189 if (OpenInternDescriptor(Mode
, compressor
) == false)
1192 (Flags
& Compressed
) == Compressed
||
1198 return FileFdError(_("Could not open file descriptor %d"), Fd
);
1202 bool FileFd::OpenInternDescriptor(unsigned int const Mode
, APT::Configuration::Compressor
const &compressor
)
1206 if (compressor
.Name
== "." || compressor
.Binary
.empty() == true)
1209 #if defined HAVE_ZLIB || defined HAVE_BZ2 || defined HAVE_LZMA
1210 // the API to open files is similar, so setup to avoid code duplicates later
1211 // and while at it ensure that we close before opening (if its a reopen)
1212 void* (*compress_open
)(int, const char *) = NULL
;
1214 /* dummy so that the rest can be 'else if's */;
1215 #define APT_COMPRESS_INIT(NAME,OPEN) \
1216 else if (compressor.Name == NAME) \
1218 compress_open = (void*(*)(int, const char *)) OPEN; \
1219 if (d != NULL) d->InternalClose(FileName); \
1222 APT_COMPRESS_INIT("gzip", gzdopen
)
1225 APT_COMPRESS_INIT("bzip2", BZ2_bzdopen
)
1228 APT_COMPRESS_INIT("xz", fdopen
)
1229 APT_COMPRESS_INIT("lzma", fdopen
)
1231 #undef APT_COMPRESS_INIT
1236 d
= new FileFdPrivate();
1238 d
->compressor
= compressor
;
1239 #if defined HAVE_ZLIB || defined HAVE_BZ2 || defined HAVE_LZMA
1240 if ((Flags
& AutoClose
) != AutoClose
&& compress_open
!= NULL
)
1242 // Need to duplicate fd here or gz/bz2 close for cleanup will close the fd as well
1243 int const internFd
= dup(iFd
);
1245 return FileFdErrno("OpenInternDescriptor", _("Could not open file descriptor %d"), iFd
);
1251 #if defined HAVE_ZLIB || defined HAVE_BZ2 || defined HAVE_LZMA
1252 if (compress_open
!= NULL
)
1254 void* compress_struct
= NULL
;
1255 if ((Mode
& ReadWrite
) == ReadWrite
)
1256 compress_struct
= compress_open(iFd
, "r+");
1257 else if ((Mode
& WriteOnly
) == WriteOnly
)
1258 compress_struct
= compress_open(iFd
, "w");
1260 compress_struct
= compress_open(iFd
, "r");
1261 if (compress_struct
== NULL
)
1265 /* dummy so that the rest can be 'else if's */;
1267 else if (compressor
.Name
== "gzip")
1268 d
->gz
= (gzFile
) compress_struct
;
1271 else if (compressor
.Name
== "bzip2")
1272 d
->bz2
= (BZFILE
*) compress_struct
;
1275 else if (compressor
.Name
== "xz" || compressor
.Name
== "lzma")
1277 uint32_t const xzlevel
= 6;
1278 uint64_t const memlimit
= UINT64_MAX
;
1279 if (d
->lzma
== NULL
)
1280 d
->lzma
= new FileFdPrivate::LZMAFILE
;
1281 d
->lzma
->file
= (FILE*) compress_struct
;
1282 lzma_stream tmp_stream
= LZMA_STREAM_INIT
;
1283 d
->lzma
->stream
= tmp_stream
;
1285 if ((Mode
& ReadWrite
) == ReadWrite
)
1286 return FileFdError("ReadWrite mode is not supported for file %s", FileName
.c_str());
1288 if ((Mode
& WriteOnly
) == WriteOnly
)
1290 if (compressor
.Name
== "xz")
1292 if (lzma_easy_encoder(&d
->lzma
->stream
, xzlevel
, LZMA_CHECK_CRC32
) != LZMA_OK
)
1297 lzma_options_lzma options
;
1298 lzma_lzma_preset(&options
, xzlevel
);
1299 if (lzma_alone_encoder(&d
->lzma
->stream
, &options
) != LZMA_OK
)
1302 d
->lzma
->compressing
= true;
1306 if (compressor
.Name
== "xz")
1308 if (lzma_auto_decoder(&d
->lzma
->stream
, memlimit
, 0) != LZMA_OK
)
1313 if (lzma_alone_decoder(&d
->lzma
->stream
, memlimit
) != LZMA_OK
)
1316 d
->lzma
->compressing
= false;
1320 Flags
|= Compressed
;
1325 // collect zombies here in case we reopen
1326 if (d
->compressor_pid
> 0)
1327 ExecWait(d
->compressor_pid
, "FileFdCompressor", true);
1329 if ((Mode
& ReadWrite
) == ReadWrite
)
1330 return FileFdError("ReadWrite mode is not supported for file %s", FileName
.c_str());
1332 bool const Comp
= (Mode
& WriteOnly
) == WriteOnly
;
1335 // Handle 'decompression' of empty files
1338 if (Buf
.st_size
== 0 && S_ISFIFO(Buf
.st_mode
) == false)
1341 // We don't need the file open - instead let the compressor open it
1342 // as he properly knows better how to efficiently read from 'his' file
1343 if (FileName
.empty() == false)
1350 // Create a data pipe
1351 int Pipe
[2] = {-1,-1};
1352 if (pipe(Pipe
) != 0)
1353 return FileFdErrno("pipe",_("Failed to create subprocess IPC"));
1354 for (int J
= 0; J
!= 2; J
++)
1355 SetCloseExec(Pipe
[J
],true);
1357 d
->compressed_fd
= iFd
;
1366 d
->compressor_pid
= ExecFork();
1367 if (d
->compressor_pid
== 0)
1371 dup2(d
->compressed_fd
,STDOUT_FILENO
);
1372 dup2(Pipe
[0],STDIN_FILENO
);
1376 if (d
->compressed_fd
!= -1)
1377 dup2(d
->compressed_fd
,STDIN_FILENO
);
1378 dup2(Pipe
[1],STDOUT_FILENO
);
1380 int const nullfd
= open("/dev/null", O_WRONLY
);
1383 dup2(nullfd
,STDERR_FILENO
);
1387 SetCloseExec(STDOUT_FILENO
,false);
1388 SetCloseExec(STDIN_FILENO
,false);
1390 std::vector
<char const*> Args
;
1391 Args
.push_back(compressor
.Binary
.c_str());
1392 std::vector
<std::string
> const * const addArgs
=
1393 (Comp
== true) ? &(compressor
.CompressArgs
) : &(compressor
.UncompressArgs
);
1394 for (std::vector
<std::string
>::const_iterator a
= addArgs
->begin();
1395 a
!= addArgs
->end(); ++a
)
1396 Args
.push_back(a
->c_str());
1397 if (Comp
== false && FileName
.empty() == false)
1399 // commands not needing arguments, do not need to be told about using standard output
1400 // in reality, only testcases with tools like cat, rev, rot13, … are able to trigger this
1401 if (compressor
.CompressArgs
.empty() == false && compressor
.UncompressArgs
.empty() == false)
1402 Args
.push_back("--stdout");
1403 if (TemporaryFileName
.empty() == false)
1404 Args
.push_back(TemporaryFileName
.c_str());
1406 Args
.push_back(FileName
.c_str());
1408 Args
.push_back(NULL
);
1410 execvp(Args
[0],(char **)&Args
[0]);
1411 cerr
<< _("Failed to exec compressor ") << Args
[0] << endl
;
1422 // FileFd::~File - Closes the file /*{{{*/
1423 // ---------------------------------------------------------------------
1424 /* If the proper modes are selected then we close the Fd and possibly
1425 unlink the file on error. */
1430 d
->CloseDown(FileName
);
1435 // FileFd::Read - Read a bit of the file /*{{{*/
1436 // ---------------------------------------------------------------------
1437 /* We are careful to handle interruption by a signal while reading
1439 bool FileFd::Read(void *To
,unsigned long long Size
,unsigned long long *Actual
)
1445 *((char *)To
) = '\0';
1449 /* dummy so that the rest can be 'else if's */;
1451 else if (d
!= NULL
&& d
->gz
!= NULL
)
1452 Res
= gzread(d
->gz
,To
,Size
);
1455 else if (d
!= NULL
&& d
->bz2
!= NULL
)
1456 Res
= BZ2_bzread(d
->bz2
,To
,Size
);
1459 else if (d
!= NULL
&& d
->lzma
!= NULL
)
1461 if (d
->lzma
->eof
== true)
1464 d
->lzma
->stream
.next_out
= (uint8_t *) To
;
1465 d
->lzma
->stream
.avail_out
= Size
;
1466 if (d
->lzma
->stream
.avail_in
== 0)
1468 d
->lzma
->stream
.next_in
= d
->lzma
->buffer
;
1469 d
->lzma
->stream
.avail_in
= fread(d
->lzma
->buffer
, 1, sizeof(d
->lzma
->buffer
)/sizeof(d
->lzma
->buffer
[0]), d
->lzma
->file
);
1471 d
->lzma
->err
= lzma_code(&d
->lzma
->stream
, LZMA_RUN
);
1472 if (d
->lzma
->err
== LZMA_STREAM_END
)
1474 d
->lzma
->eof
= true;
1475 Res
= Size
- d
->lzma
->stream
.avail_out
;
1477 else if (d
->lzma
->err
!= LZMA_OK
)
1484 Res
= Size
- d
->lzma
->stream
.avail_out
;
1487 // lzma run was okay, but produced no output…
1495 Res
= read(iFd
,To
,Size
);
1501 // trick the while-loop into running again
1507 /* dummy so that the rest can be 'else if's */;
1509 else if (d
!= NULL
&& d
->gz
!= NULL
)
1512 char const * const errmsg
= gzerror(d
->gz
, &err
);
1514 return FileFdError("gzread: %s (%d: %s)", _("Read error"), err
, errmsg
);
1518 else if (d
!= NULL
&& d
->bz2
!= NULL
)
1521 char const * const errmsg
= BZ2_bzerror(d
->bz2
, &err
);
1522 if (err
!= BZ_IO_ERROR
)
1523 return FileFdError("BZ2_bzread: %s (%d: %s)", _("Read error"), err
, errmsg
);
1527 else if (d
!= NULL
&& d
->lzma
!= NULL
)
1528 return FileFdError("lzma_read: %s (%d)", _("Read error"), d
->lzma
->err
);
1530 return FileFdErrno("read",_("Read error"));
1533 To
= (char *)To
+ Res
;
1540 while (Res
> 0 && Size
> 0);
1552 return FileFdError(_("read, still have %llu to read but none left"), Size
);
1555 // FileFd::ReadLine - Read a complete line from the file /*{{{*/
1556 // ---------------------------------------------------------------------
1557 /* Beware: This method can be quiet slow for big buffers on UNcompressed
1558 files because of the naive implementation! */
1559 char* FileFd::ReadLine(char *To
, unsigned long long const Size
)
1563 if (d
!= NULL
&& d
->gz
!= NULL
)
1564 return gzgets(d
->gz
, To
, Size
);
1567 unsigned long long read
= 0;
1568 while ((Size
- 1) != read
)
1570 unsigned long long done
= 0;
1571 if (Read(To
+ read
, 1, &done
) == false)
1575 if (To
[read
++] == '\n')
1584 // FileFd::Write - Write to the file /*{{{*/
1585 // ---------------------------------------------------------------------
1587 bool FileFd::Write(const void *From
,unsigned long long Size
)
1594 /* dummy so that the rest can be 'else if's */;
1596 else if (d
!= NULL
&& d
->gz
!= NULL
)
1597 Res
= gzwrite(d
->gz
,From
,Size
);
1600 else if (d
!= NULL
&& d
->bz2
!= NULL
)
1601 Res
= BZ2_bzwrite(d
->bz2
,(void*)From
,Size
);
1604 else if (d
!= NULL
&& d
->lzma
!= NULL
)
1606 d
->lzma
->stream
.next_in
= (uint8_t *)From
;
1607 d
->lzma
->stream
.avail_in
= Size
;
1608 d
->lzma
->stream
.next_out
= d
->lzma
->buffer
;
1609 d
->lzma
->stream
.avail_out
= sizeof(d
->lzma
->buffer
)/sizeof(d
->lzma
->buffer
[0]);
1610 d
->lzma
->err
= lzma_code(&d
->lzma
->stream
, LZMA_RUN
);
1611 if (d
->lzma
->err
!= LZMA_OK
)
1613 size_t const n
= sizeof(d
->lzma
->buffer
)/sizeof(d
->lzma
->buffer
[0]) - d
->lzma
->stream
.avail_out
;
1614 size_t const m
= (n
== 0) ? 0 : fwrite(d
->lzma
->buffer
, 1, n
, d
->lzma
->file
);
1618 Res
= Size
- d
->lzma
->stream
.avail_in
;
1622 Res
= write(iFd
,From
,Size
);
1624 if (Res
< 0 && errno
== EINTR
)
1629 /* dummy so that the rest can be 'else if's */;
1631 else if (d
!= NULL
&& d
->gz
!= NULL
)
1634 char const * const errmsg
= gzerror(d
->gz
, &err
);
1636 return FileFdError("gzwrite: %s (%d: %s)", _("Write error"), err
, errmsg
);
1640 else if (d
!= NULL
&& d
->bz2
!= NULL
)
1643 char const * const errmsg
= BZ2_bzerror(d
->bz2
, &err
);
1644 if (err
!= BZ_IO_ERROR
)
1645 return FileFdError("BZ2_bzwrite: %s (%d: %s)", _("Write error"), err
, errmsg
);
1649 else if (d
!= NULL
&& d
->lzma
!= NULL
)
1650 return FileFdErrno("lzma_fwrite", _("Write error"));
1652 return FileFdErrno("write",_("Write error"));
1655 From
= (char const *)From
+ Res
;
1660 while (Res
> 0 && Size
> 0);
1665 return FileFdError(_("write, still have %llu to write but couldn't"), Size
);
1667 bool FileFd::Write(int Fd
, const void *From
, unsigned long long Size
)
1673 Res
= write(Fd
,From
,Size
);
1674 if (Res
< 0 && errno
== EINTR
)
1677 return _error
->Errno("write",_("Write error"));
1679 From
= (char const *)From
+ Res
;
1682 while (Res
> 0 && Size
> 0);
1687 return _error
->Error(_("write, still have %llu to write but couldn't"), Size
);
1690 // FileFd::Seek - Seek in the file /*{{{*/
1691 // ---------------------------------------------------------------------
1693 bool FileFd::Seek(unsigned long long To
)
1697 if (d
!= NULL
&& (d
->pipe
== true || d
->InternalStream() == true))
1699 // Our poor man seeking in pipes is costly, so try to avoid it
1700 unsigned long long seekpos
= Tell();
1703 else if (seekpos
< To
)
1704 return Skip(To
- seekpos
);
1706 if ((d
->openmode
& ReadOnly
) != ReadOnly
)
1707 return FileFdError("Reopen is only implemented for read-only files!");
1708 d
->InternalClose(FileName
);
1712 if (TemporaryFileName
.empty() == false)
1713 iFd
= open(TemporaryFileName
.c_str(), O_RDONLY
);
1714 else if (FileName
.empty() == false)
1715 iFd
= open(FileName
.c_str(), O_RDONLY
);
1718 if (d
->compressed_fd
> 0)
1719 if (lseek(d
->compressed_fd
, 0, SEEK_SET
) != 0)
1720 iFd
= d
->compressed_fd
;
1722 return FileFdError("Reopen is not implemented for pipes opened with FileFd::OpenDescriptor()!");
1725 if (OpenInternDescriptor(d
->openmode
, d
->compressor
) == false)
1726 return FileFdError("Seek on file %s because it couldn't be reopened", FileName
.c_str());
1736 if (d
!= NULL
&& d
->gz
)
1737 res
= gzseek(d
->gz
,To
,SEEK_SET
);
1740 res
= lseek(iFd
,To
,SEEK_SET
);
1741 if (res
!= (off_t
)To
)
1742 return FileFdError("Unable to seek to %llu", To
);
1749 // FileFd::Skip - Seek in the file /*{{{*/
1750 // ---------------------------------------------------------------------
1752 bool FileFd::Skip(unsigned long long Over
)
1754 if (d
!= NULL
&& (d
->pipe
== true || d
->InternalStream() == true))
1759 unsigned long long toread
= std::min((unsigned long long) sizeof(buffer
), Over
);
1760 if (Read(buffer
, toread
) == false)
1761 return FileFdError("Unable to seek ahead %llu",Over
);
1769 if (d
!= NULL
&& d
->gz
!= NULL
)
1770 res
= gzseek(d
->gz
,Over
,SEEK_CUR
);
1773 res
= lseek(iFd
,Over
,SEEK_CUR
);
1775 return FileFdError("Unable to seek ahead %llu",Over
);
1782 // FileFd::Truncate - Truncate the file /*{{{*/
1783 // ---------------------------------------------------------------------
1785 bool FileFd::Truncate(unsigned long long To
)
1787 // truncating /dev/null is always successful - as we get an error otherwise
1788 if (To
== 0 && FileName
== "/dev/null")
1790 #if defined HAVE_ZLIB || defined HAVE_BZ2 || defined HAVE_LZMA
1791 if (d
!= NULL
&& (d
->InternalStream() == true
1796 return FileFdError("Truncating compressed files is not implemented (%s)", FileName
.c_str());
1798 if (ftruncate(iFd
,To
) != 0)
1799 return FileFdError("Unable to truncate to %llu",To
);
1804 // FileFd::Tell - Current seek position /*{{{*/
1805 // ---------------------------------------------------------------------
1807 unsigned long long FileFd::Tell()
1809 // In theory, we could just return seekpos here always instead of
1810 // seeking around, but not all users of FileFd use always Seek() and co
1811 // so d->seekpos isn't always true and we can just use it as a hint if
1812 // we have nothing else, but not always as an authority…
1813 if (d
!= NULL
&& (d
->pipe
== true || d
->InternalStream() == true))
1818 if (d
!= NULL
&& d
->gz
!= NULL
)
1819 Res
= gztell(d
->gz
);
1822 Res
= lseek(iFd
,0,SEEK_CUR
);
1823 if (Res
== (off_t
)-1)
1824 FileFdErrno("lseek","Failed to determine the current file position");
1830 static bool StatFileFd(char const * const msg
, int const iFd
, std::string
const &FileName
, struct stat
&Buf
, FileFdPrivate
* const d
) /*{{{*/
1832 bool ispipe
= (d
!= NULL
&& d
->pipe
== true);
1833 if (ispipe
== false)
1835 if (fstat(iFd
,&Buf
) != 0)
1836 // higher-level code will generate more meaningful messages,
1837 // even translated this would be meaningless for users
1838 return _error
->Errno("fstat", "Unable to determine %s for fd %i", msg
, iFd
);
1839 if (FileName
.empty() == false)
1840 ispipe
= S_ISFIFO(Buf
.st_mode
);
1843 // for compressor pipes st_size is undefined and at 'best' zero
1846 // we set it here, too, as we get the info here for free
1847 // in theory the Open-methods should take care of it already
1850 if (stat(FileName
.c_str(), &Buf
) != 0)
1851 return _error
->Errno("fstat", "Unable to determine %s for file %s", msg
, FileName
.c_str());
1856 // FileFd::FileSize - Return the size of the file /*{{{*/
1857 unsigned long long FileFd::FileSize()
1860 if (StatFileFd("file size", iFd
, FileName
, Buf
, d
) == false)
1868 // FileFd::ModificationTime - Return the time of last touch /*{{{*/
1869 time_t FileFd::ModificationTime()
1872 if (StatFileFd("modification time", iFd
, FileName
, Buf
, d
) == false)
1877 return Buf
.st_mtime
;
1880 // FileFd::Size - Return the size of the content in the file /*{{{*/
1881 // ---------------------------------------------------------------------
1883 unsigned long long FileFd::Size()
1885 unsigned long long size
= FileSize();
1887 // for compressor pipes st_size is undefined and at 'best' zero,
1888 // so we 'read' the content and 'seek' back - see there
1889 if (d
!= NULL
&& (d
->pipe
== true || (d
->InternalStream() == true && size
> 0)))
1891 unsigned long long const oldSeek
= Tell();
1893 unsigned long long read
= 0;
1895 if (Read(ignore
, sizeof(ignore
), &read
) == false)
1905 // only check gzsize if we are actually a gzip file, just checking for
1906 // "gz" is not sufficient as uncompressed files could be opened with
1907 // gzopen in "direct" mode as well
1908 else if (d
!= NULL
&& d
->gz
&& !gzdirect(d
->gz
) && size
> 0)
1910 off_t
const oldPos
= lseek(iFd
,0,SEEK_CUR
);
1911 /* unfortunately zlib.h doesn't provide a gzsize(), so we have to do
1912 * this ourselves; the original (uncompressed) file size is the last 32
1913 * bits of the file */
1914 // FIXME: Size for gz-files is limited by 32bit… no largefile support
1915 if (lseek(iFd
, -4, SEEK_END
) < 0)
1917 FileFdErrno("lseek","Unable to seek to end of gzipped file");
1921 if (read(iFd
, &size
, 4) != 4)
1923 FileFdErrno("read","Unable to read original size of gzipped file");
1926 size
= le32toh(size
);
1928 if (lseek(iFd
, oldPos
, SEEK_SET
) < 0)
1930 FileFdErrno("lseek","Unable to seek in gzipped file");
1941 // FileFd::Close - Close the file if the close flag is set /*{{{*/
1942 // ---------------------------------------------------------------------
1944 bool FileFd::Close()
1950 if ((Flags
& AutoClose
) == AutoClose
)
1952 if ((Flags
& Compressed
) != Compressed
&& iFd
> 0 && close(iFd
) != 0)
1953 Res
&= _error
->Errno("close",_("Problem closing the file %s"), FileName
.c_str());
1956 Res
&= d
->CloseDown(FileName
);
1962 if ((Flags
& Replace
) == Replace
) {
1963 if (rename(TemporaryFileName
.c_str(), FileName
.c_str()) != 0)
1964 Res
&= _error
->Errno("rename",_("Problem renaming the file %s to %s"), TemporaryFileName
.c_str(), FileName
.c_str());
1966 FileName
= TemporaryFileName
; // for the unlink() below.
1967 TemporaryFileName
.clear();
1972 if ((Flags
& Fail
) == Fail
&& (Flags
& DelOnFail
) == DelOnFail
&&
1973 FileName
.empty() == false)
1974 if (unlink(FileName
.c_str()) != 0)
1975 Res
&= _error
->WarningE("unlnk",_("Problem unlinking the file %s"), FileName
.c_str());
1982 // FileFd::Sync - Sync the file /*{{{*/
1983 // ---------------------------------------------------------------------
1987 if (fsync(iFd
) != 0)
1988 return FileFdErrno("sync",_("Problem syncing the file"));
1992 // FileFd::FileFdErrno - set Fail and call _error->Errno *{{{*/
1993 bool FileFd::FileFdErrno(const char *Function
, const char *Description
,...)
1997 size_t msgSize
= 400;
1998 int const errsv
= errno
;
2001 va_start(args
,Description
);
2002 if (_error
->InsertErrno(GlobalError::ERROR
, Function
, Description
, args
, errsv
, msgSize
) == false)
2009 // FileFd::FileFdError - set Fail and call _error->Error *{{{*/
2010 bool FileFd::FileFdError(const char *Description
,...) {
2013 size_t msgSize
= 400;
2016 va_start(args
,Description
);
2017 if (_error
->Insert(GlobalError::ERROR
, Description
, args
, msgSize
) == false)
2025 APT_DEPRECATED gzFile
FileFd::gzFd() {
2034 // Glob - wrapper around "glob()" /*{{{*/
2035 // ---------------------------------------------------------------------
2037 std::vector
<std::string
> Glob(std::string
const &pattern
, int flags
)
2039 std::vector
<std::string
> result
;
2044 glob_res
= glob(pattern
.c_str(), flags
, NULL
, &globbuf
);
2048 if(glob_res
!= GLOB_NOMATCH
) {
2049 _error
->Errno("glob", "Problem with glob");
2055 for(i
=0;i
<globbuf
.gl_pathc
;i
++)
2056 result
.push_back(string(globbuf
.gl_pathv
[i
]));
2063 std::string
GetTempDir()
2065 const char *tmpdir
= getenv("TMPDIR");
2072 // check that tmpdir is set and exists
2074 if (!tmpdir
|| strlen(tmpdir
) == 0 || stat(tmpdir
, &st
) != 0)
2077 return string(tmpdir
);
2080 FileFd
* GetTempFile(std::string
const &Prefix
, bool ImmediateUnlink
)
2083 FileFd
*Fd
= new FileFd();
2085 std::string tempdir
= GetTempDir();
2086 snprintf(fn
, sizeof(fn
), "%s/%s.XXXXXX",
2087 tempdir
.c_str(), Prefix
.c_str());
2088 int fd
= mkstemp(fn
);
2093 _error
->Errno("GetTempFile",_("Unable to mkstemp %s"), fn
);
2096 if (!Fd
->OpenDescriptor(fd
, FileFd::WriteOnly
, FileFd::None
, true))
2098 _error
->Errno("GetTempFile",_("Unable to write to %s"),fn
);
2105 bool Rename(std::string From
, std::string To
)
2107 if (rename(From
.c_str(),To
.c_str()) != 0)
2109 _error
->Error(_("rename failed, %s (%s -> %s)."),strerror(errno
),
2110 From
.c_str(),To
.c_str());
2116 bool Popen(const char* Args
[], FileFd
&Fd
, pid_t
&Child
, FileFd::OpenMode Mode
)
2119 if (Mode
!= FileFd::ReadOnly
&& Mode
!= FileFd::WriteOnly
)
2120 return _error
->Error("Popen supports ReadOnly (x)or WriteOnly mode only");
2122 int Pipe
[2] = {-1, -1};
2125 return _error
->Errno("pipe", _("Failed to create subprocess IPC"));
2128 std::set
<int> keep_fds
;
2129 keep_fds
.insert(Pipe
[0]);
2130 keep_fds
.insert(Pipe
[1]);
2131 Child
= ExecFork(keep_fds
);
2133 return _error
->Errno("fork", "Failed to fork");
2136 if(Mode
== FileFd::ReadOnly
)
2141 else if(Mode
== FileFd::WriteOnly
)
2147 if(Mode
== FileFd::ReadOnly
)
2151 } else if(Mode
== FileFd::WriteOnly
)
2154 execv(Args
[0], (char**)Args
);
2157 if(Mode
== FileFd::ReadOnly
)
2161 } else if(Mode
== FileFd::WriteOnly
)
2166 Fd
.OpenDescriptor(fd
, Mode
, FileFd::None
, true);
2176 const std::string nobody
= _config
->Find("APT::User::Nobody", "nobody");
2177 struct passwd
*pw
= getpwnam(nobody
.c_str());
2179 return _error
->Warning("No user %s, can not drop rights", nobody
.c_str());
2180 if (setgid(pw
->pw_gid
) != 0)
2181 return _error
->Errno("setgid", "Failed to setgid");
2182 if (setuid(pw
->pw_uid
) != 0)
2183 return _error
->Errno("setuid", "Failed to setuid");