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>
69 #include <sys/prctl.h>
77 // RunScripts - Run a set of scripts from a configuration subtree /*{{{*/
78 // ---------------------------------------------------------------------
80 bool RunScripts(const char *Cnf
)
82 Configuration::Item
const *Opts
= _config
->Tree(Cnf
);
83 if (Opts
== 0 || Opts
->Child
== 0)
87 // Fork for running the system calls
88 pid_t Child
= ExecFork();
93 if (_config
->FindDir("DPkg::Chroot-Directory","/") != "/")
95 std::cerr
<< "Chrooting into "
96 << _config
->FindDir("DPkg::Chroot-Directory")
98 if (chroot(_config
->FindDir("DPkg::Chroot-Directory","/").c_str()) != 0)
102 if (chdir("/tmp/") != 0)
105 unsigned int Count
= 1;
106 for (; Opts
!= 0; Opts
= Opts
->Next
, Count
++)
108 if (Opts
->Value
.empty() == true)
111 if(_config
->FindB("Debug::RunScripts", false) == true)
112 std::clog
<< "Running external script: '"
113 << Opts
->Value
<< "'" << std::endl
;
115 if (system(Opts
->Value
.c_str()) != 0)
121 // Wait for the child
123 while (waitpid(Child
,&Status
,0) != Child
)
127 return _error
->Errno("waitpid","Couldn't wait for subprocess");
130 // Restore sig int/quit
131 signal(SIGQUIT
,SIG_DFL
);
132 signal(SIGINT
,SIG_DFL
);
134 // Check for an error code.
135 if (WIFEXITED(Status
) == 0 || WEXITSTATUS(Status
) != 0)
137 unsigned int Count
= WEXITSTATUS(Status
);
141 for (; Opts
!= 0 && Count
!= 1; Opts
= Opts
->Next
, Count
--);
142 _error
->Error("Problem executing scripts %s '%s'",Cnf
,Opts
->Value
.c_str());
145 return _error
->Error("Sub-process returned an error code");
152 // CopyFile - Buffered copy of a file /*{{{*/
153 // ---------------------------------------------------------------------
154 /* The caller is expected to set things so that failure causes erasure */
155 bool CopyFile(FileFd
&From
,FileFd
&To
)
157 if (From
.IsOpen() == false || To
.IsOpen() == false ||
158 From
.Failed() == true || To
.Failed() == true)
161 // Buffered copy between fds
162 SPtrArray
<unsigned char> Buf
= new unsigned char[64000];
163 unsigned long long Size
= From
.Size();
166 unsigned long long ToRead
= Size
;
170 if (From
.Read(Buf
,ToRead
) == false ||
171 To
.Write(Buf
,ToRead
) == false)
180 // GetLock - Gets a lock file /*{{{*/
181 // ---------------------------------------------------------------------
182 /* This will create an empty file of the given name and lock it. Once this
183 is done all other calls to GetLock in any other process will fail with
184 -1. The return result is the fd of the file, the call should call
185 close at some time. */
186 int GetLock(string File
,bool Errors
)
188 // GetLock() is used in aptitude on directories with public-write access
189 // Use O_NOFOLLOW here to prevent symlink traversal attacks
190 int FD
= open(File
.c_str(),O_RDWR
| O_CREAT
| O_NOFOLLOW
,0640);
193 // Read only .. can't have locking problems there.
196 _error
->Warning(_("Not using locking for read only lock file %s"),File
.c_str());
197 return dup(0); // Need something for the caller to close
201 _error
->Errno("open",_("Could not open lock file %s"),File
.c_str());
203 // Feh.. We do this to distinguish the lock vs open case..
207 SetCloseExec(FD
,true);
209 // Acquire a write lock
212 fl
.l_whence
= SEEK_SET
;
215 if (fcntl(FD
,F_SETLK
,&fl
) == -1)
217 // always close to not leak resources
224 _error
->Warning(_("Not using locking for nfs mounted lock file %s"),File
.c_str());
225 return dup(0); // Need something for the caller to close
229 _error
->Errno("open",_("Could not get lock %s"),File
.c_str());
237 // FileExists - Check if a file exists /*{{{*/
238 // ---------------------------------------------------------------------
239 /* Beware: Directories are also files! */
240 bool FileExists(string File
)
243 if (stat(File
.c_str(),&Buf
) != 0)
248 // RealFileExists - Check if a file exists and if it is really a file /*{{{*/
249 // ---------------------------------------------------------------------
251 bool RealFileExists(string File
)
254 if (stat(File
.c_str(),&Buf
) != 0)
256 return ((Buf
.st_mode
& S_IFREG
) != 0);
259 // DirectoryExists - Check if a directory exists and is really one /*{{{*/
260 // ---------------------------------------------------------------------
262 bool DirectoryExists(string
const &Path
)
265 if (stat(Path
.c_str(),&Buf
) != 0)
267 return ((Buf
.st_mode
& S_IFDIR
) != 0);
270 // CreateDirectory - poor man's mkdir -p guarded by a parent directory /*{{{*/
271 // ---------------------------------------------------------------------
272 /* This method will create all directories needed for path in good old
273 mkdir -p style but refuses to do this if Parent is not a prefix of
274 this Path. Example: /var/cache/ and /var/cache/apt/archives are given,
275 so it will create apt/archives if /var/cache exists - on the other
276 hand if the parent is /var/lib the creation will fail as this path
277 is not a parent of the path to be generated. */
278 bool CreateDirectory(string
const &Parent
, string
const &Path
)
280 if (Parent
.empty() == true || Path
.empty() == true)
283 if (DirectoryExists(Path
) == true)
286 if (DirectoryExists(Parent
) == false)
289 // we are not going to create directories "into the blue"
290 if (Path
.compare(0, Parent
.length(), Parent
) != 0)
293 vector
<string
> const dirs
= VectorizeString(Path
.substr(Parent
.size()), '/');
294 string progress
= Parent
;
295 for (vector
<string
>::const_iterator d
= dirs
.begin(); d
!= dirs
.end(); ++d
)
297 if (d
->empty() == true)
300 progress
.append("/").append(*d
);
301 if (DirectoryExists(progress
) == true)
304 if (mkdir(progress
.c_str(), 0755) != 0)
310 // CreateAPTDirectoryIfNeeded - ensure that the given directory exists /*{{{*/
311 // ---------------------------------------------------------------------
312 /* a small wrapper around CreateDirectory to check if it exists and to
313 remove the trailing "/apt/" from the parent directory if needed */
314 bool CreateAPTDirectoryIfNeeded(string
const &Parent
, string
const &Path
)
316 if (DirectoryExists(Path
) == true)
319 size_t const len
= Parent
.size();
320 if (len
> 5 && Parent
.find("/apt/", len
- 6, 5) == len
- 5)
322 if (CreateDirectory(Parent
.substr(0,len
-5), Path
) == true)
325 else if (CreateDirectory(Parent
, Path
) == true)
331 // GetListOfFilesInDir - returns a vector of files in the given dir /*{{{*/
332 // ---------------------------------------------------------------------
333 /* If an extension is given only files with this extension are included
334 in the returned vector, otherwise every "normal" file is included. */
335 std::vector
<string
> GetListOfFilesInDir(string
const &Dir
, string
const &Ext
,
336 bool const &SortList
, bool const &AllowNoExt
)
338 std::vector
<string
> ext
;
340 if (Ext
.empty() == false)
342 if (AllowNoExt
== true && ext
.empty() == false)
344 return GetListOfFilesInDir(Dir
, ext
, SortList
);
346 std::vector
<string
> GetListOfFilesInDir(string
const &Dir
, std::vector
<string
> const &Ext
,
347 bool const &SortList
)
349 // Attention debuggers: need to be set with the environment config file!
350 bool const Debug
= _config
->FindB("Debug::GetListOfFilesInDir", false);
353 std::clog
<< "Accept in " << Dir
<< " only files with the following " << Ext
.size() << " extensions:" << std::endl
;
354 if (Ext
.empty() == true)
355 std::clog
<< "\tNO extension" << std::endl
;
357 for (std::vector
<string
>::const_iterator e
= Ext
.begin();
359 std::clog
<< '\t' << (e
->empty() == true ? "NO" : *e
) << " extension" << std::endl
;
362 std::vector
<string
> List
;
364 if (DirectoryExists(Dir
) == false)
366 _error
->Error(_("List of files can't be created as '%s' is not a directory"), Dir
.c_str());
370 Configuration::MatchAgainstConfig
SilentIgnore("Dir::Ignore-Files-Silently");
371 DIR *D
= opendir(Dir
.c_str());
374 _error
->Errno("opendir",_("Unable to read %s"),Dir
.c_str());
378 for (struct dirent
*Ent
= readdir(D
); Ent
!= 0; Ent
= readdir(D
))
380 // skip "hidden" files
381 if (Ent
->d_name
[0] == '.')
384 // Make sure it is a file and not something else
385 string
const File
= flCombine(Dir
,Ent
->d_name
);
386 #ifdef _DIRENT_HAVE_D_TYPE
387 if (Ent
->d_type
!= DT_REG
)
390 if (RealFileExists(File
) == false)
392 // do not show ignoration warnings for directories
394 #ifdef _DIRENT_HAVE_D_TYPE
395 Ent
->d_type
== DT_DIR
||
397 DirectoryExists(File
) == true)
399 if (SilentIgnore
.Match(Ent
->d_name
) == false)
400 _error
->Notice(_("Ignoring '%s' in directory '%s' as it is not a regular file"), Ent
->d_name
, Dir
.c_str());
405 // check for accepted extension:
406 // no extension given -> periods are bad as hell!
407 // extensions given -> "" extension allows no extension
408 if (Ext
.empty() == false)
410 string d_ext
= flExtension(Ent
->d_name
);
411 if (d_ext
== Ent
->d_name
) // no extension
413 if (std::find(Ext
.begin(), Ext
.end(), "") == Ext
.end())
416 std::clog
<< "Bad file: " << Ent
->d_name
<< " → no extension" << std::endl
;
417 if (SilentIgnore
.Match(Ent
->d_name
) == false)
418 _error
->Notice(_("Ignoring file '%s' in directory '%s' as it has no filename extension"), Ent
->d_name
, Dir
.c_str());
422 else if (std::find(Ext
.begin(), Ext
.end(), d_ext
) == Ext
.end())
425 std::clog
<< "Bad file: " << Ent
->d_name
<< " → bad extension »" << flExtension(Ent
->d_name
) << "«" << std::endl
;
426 if (SilentIgnore
.Match(Ent
->d_name
) == false)
427 _error
->Notice(_("Ignoring file '%s' in directory '%s' as it has an invalid filename extension"), Ent
->d_name
, Dir
.c_str());
432 // Skip bad filenames ala run-parts
433 const char *C
= Ent
->d_name
;
435 if (isalpha(*C
) == 0 && isdigit(*C
) == 0
436 && *C
!= '_' && *C
!= '-' && *C
!= ':') {
437 // no required extension -> dot is a bad character
438 if (*C
== '.' && Ext
.empty() == false)
443 // we don't reach the end of the name -> bad character included
447 std::clog
<< "Bad file: " << Ent
->d_name
<< " → bad character »"
448 << *C
<< "« in filename (period allowed: " << (Ext
.empty() ? "no" : "yes") << ")" << std::endl
;
452 // skip filenames which end with a period. These are never valid
456 std::clog
<< "Bad file: " << Ent
->d_name
<< " → Period as last character" << std::endl
;
461 std::clog
<< "Accept file: " << Ent
->d_name
<< " in " << Dir
<< std::endl
;
462 List
.push_back(File
);
466 if (SortList
== true)
467 std::sort(List
.begin(),List
.end());
470 std::vector
<string
> GetListOfFilesInDir(string
const &Dir
, bool SortList
)
472 bool const Debug
= _config
->FindB("Debug::GetListOfFilesInDir", false);
474 std::clog
<< "Accept in " << Dir
<< " all regular files" << std::endl
;
476 std::vector
<string
> List
;
478 if (DirectoryExists(Dir
) == false)
480 _error
->Error(_("List of files can't be created as '%s' is not a directory"), Dir
.c_str());
484 DIR *D
= opendir(Dir
.c_str());
487 _error
->Errno("opendir",_("Unable to read %s"),Dir
.c_str());
491 for (struct dirent
*Ent
= readdir(D
); Ent
!= 0; Ent
= readdir(D
))
493 // skip "hidden" files
494 if (Ent
->d_name
[0] == '.')
497 // Make sure it is a file and not something else
498 string
const File
= flCombine(Dir
,Ent
->d_name
);
499 #ifdef _DIRENT_HAVE_D_TYPE
500 if (Ent
->d_type
!= DT_REG
)
503 if (RealFileExists(File
) == false)
506 std::clog
<< "Bad file: " << Ent
->d_name
<< " → it is not a real file" << std::endl
;
511 // Skip bad filenames ala run-parts
512 const char *C
= Ent
->d_name
;
514 if (isalpha(*C
) == 0 && isdigit(*C
) == 0
515 && *C
!= '_' && *C
!= '-' && *C
!= '.')
518 // we don't reach the end of the name -> bad character included
522 std::clog
<< "Bad file: " << Ent
->d_name
<< " → bad character »" << *C
<< "« in filename" << std::endl
;
526 // skip filenames which end with a period. These are never valid
530 std::clog
<< "Bad file: " << Ent
->d_name
<< " → Period as last character" << std::endl
;
535 std::clog
<< "Accept file: " << Ent
->d_name
<< " in " << Dir
<< std::endl
;
536 List
.push_back(File
);
540 if (SortList
== true)
541 std::sort(List
.begin(),List
.end());
545 // SafeGetCWD - This is a safer getcwd that returns a dynamic string /*{{{*/
546 // ---------------------------------------------------------------------
547 /* We return / on failure. */
550 // Stash the current dir.
553 if (getcwd(S
,sizeof(S
)-2) == 0)
555 unsigned int Len
= strlen(S
);
561 // GetModificationTime - Get the mtime of the given file or -1 on error /*{{{*/
562 // ---------------------------------------------------------------------
563 /* We return / on failure. */
564 time_t GetModificationTime(string
const &Path
)
567 if (stat(Path
.c_str(), &St
) < 0)
572 // flNotDir - Strip the directory from the filename /*{{{*/
573 // ---------------------------------------------------------------------
575 string
flNotDir(string File
)
577 string::size_type Res
= File
.rfind('/');
578 if (Res
== string::npos
)
581 return string(File
,Res
,Res
- File
.length());
584 // flNotFile - Strip the file from the directory name /*{{{*/
585 // ---------------------------------------------------------------------
586 /* Result ends in a / */
587 string
flNotFile(string File
)
589 string::size_type Res
= File
.rfind('/');
590 if (Res
== string::npos
)
593 return string(File
,0,Res
);
596 // flExtension - Return the extension for the file /*{{{*/
597 // ---------------------------------------------------------------------
599 string
flExtension(string File
)
601 string::size_type Res
= File
.rfind('.');
602 if (Res
== string::npos
)
605 return string(File
,Res
,Res
- File
.length());
608 // flNoLink - If file is a symlink then deref it /*{{{*/
609 // ---------------------------------------------------------------------
610 /* If the name is not a link then the returned path is the input. */
611 string
flNoLink(string File
)
614 if (lstat(File
.c_str(),&St
) != 0 || S_ISLNK(St
.st_mode
) == 0)
616 if (stat(File
.c_str(),&St
) != 0)
619 /* Loop resolving the link. There is no need to limit the number of
620 loops because the stat call above ensures that the symlink is not
628 if ((Res
= readlink(NFile
.c_str(),Buffer
,sizeof(Buffer
))) <= 0 ||
629 (size_t)Res
>= sizeof(Buffer
))
632 // Append or replace the previous path
634 if (Buffer
[0] == '/')
637 NFile
= flNotFile(NFile
) + Buffer
;
639 // See if we are done
640 if (lstat(NFile
.c_str(),&St
) != 0)
642 if (S_ISLNK(St
.st_mode
) == 0)
647 // flCombine - Combine a file and a directory /*{{{*/
648 // ---------------------------------------------------------------------
649 /* If the file is an absolute path then it is just returned, otherwise
650 the directory is pre-pended to it. */
651 string
flCombine(string Dir
,string File
)
653 if (File
.empty() == true)
656 if (File
[0] == '/' || Dir
.empty() == true)
658 if (File
.length() >= 2 && File
[0] == '.' && File
[1] == '/')
660 if (Dir
[Dir
.length()-1] == '/')
662 return Dir
+ '/' + File
;
665 // flAbsPath - Return the absolute path of the filename /*{{{*/
666 // ---------------------------------------------------------------------
668 string
flAbsPath(string File
)
670 char *p
= realpath(File
.c_str(), NULL
);
673 _error
->Errno("realpath", "flAbsPath failed");
676 std::string
AbsPath(p
);
681 // SetCloseExec - Set the close on exec flag /*{{{*/
682 // ---------------------------------------------------------------------
684 void SetCloseExec(int Fd
,bool Close
)
686 if (fcntl(Fd
,F_SETFD
,(Close
== false)?0:FD_CLOEXEC
) != 0)
688 cerr
<< "FATAL -> Could not set close on exec " << strerror(errno
) << endl
;
693 // SetNonBlock - Set the nonblocking flag /*{{{*/
694 // ---------------------------------------------------------------------
696 void SetNonBlock(int Fd
,bool Block
)
698 int Flags
= fcntl(Fd
,F_GETFL
) & (~O_NONBLOCK
);
699 if (fcntl(Fd
,F_SETFL
,Flags
| ((Block
== false)?0:O_NONBLOCK
)) != 0)
701 cerr
<< "FATAL -> Could not set non-blocking flag " << strerror(errno
) << endl
;
706 // WaitFd - Wait for a FD to become readable /*{{{*/
707 // ---------------------------------------------------------------------
708 /* This waits for a FD to become readable using select. It is useful for
709 applications making use of non-blocking sockets. The timeout is
711 bool WaitFd(int Fd
,bool write
,unsigned long timeout
)
724 Res
= select(Fd
+1,0,&Set
,0,(timeout
!= 0?&tv
:0));
726 while (Res
< 0 && errno
== EINTR
);
736 Res
= select(Fd
+1,&Set
,0,0,(timeout
!= 0?&tv
:0));
738 while (Res
< 0 && errno
== EINTR
);
747 // MergeKeepFdsFromConfiguration - Merge APT::Keep-Fds configuration /*{{{*/
748 // ---------------------------------------------------------------------
749 /* This is used to merge the APT::Keep-Fds with the provided KeepFDs
752 void MergeKeepFdsFromConfiguration(std::set
<int> &KeepFDs
)
754 Configuration::Item
const *Opts
= _config
->Tree("APT::Keep-Fds");
755 if (Opts
!= 0 && Opts
->Child
!= 0)
758 for (; Opts
!= 0; Opts
= Opts
->Next
)
760 if (Opts
->Value
.empty() == true)
762 int fd
= atoi(Opts
->Value
.c_str());
768 // ExecFork - Magical fork that sanitizes the context before execing /*{{{*/
769 // ---------------------------------------------------------------------
770 /* This is used if you want to cleanse the environment for the forked
771 child, it fixes up the important signals and nukes all of the fds,
772 otherwise acts like normal fork. */
776 // we need to merge the Keep-Fds as external tools like
777 // debconf-apt-progress use it
778 MergeKeepFdsFromConfiguration(KeepFDs
);
779 return ExecFork(KeepFDs
);
782 pid_t
ExecFork(std::set
<int> KeepFDs
)
784 // Fork off the process
785 pid_t Process
= fork();
788 cerr
<< "FATAL -> Failed to fork." << endl
;
792 // Spawn the subprocess
796 signal(SIGPIPE
,SIG_DFL
);
797 signal(SIGQUIT
,SIG_DFL
);
798 signal(SIGINT
,SIG_DFL
);
799 signal(SIGWINCH
,SIG_DFL
);
800 signal(SIGCONT
,SIG_DFL
);
801 signal(SIGTSTP
,SIG_DFL
);
803 // Close all of our FDs - just in case
804 for (int K
= 3; K
!= sysconf(_SC_OPEN_MAX
); K
++)
806 if(KeepFDs
.find(K
) == KeepFDs
.end())
807 fcntl(K
,F_SETFD
,FD_CLOEXEC
);
814 // ExecWait - Fancy waitpid /*{{{*/
815 // ---------------------------------------------------------------------
816 /* Waits for the given sub process. If Reap is set then no errors are
817 generated. Otherwise a failed subprocess will generate a proper descriptive
819 bool ExecWait(pid_t Pid
,const char *Name
,bool Reap
)
824 // Wait and collect the error code
826 while (waitpid(Pid
,&Status
,0) != Pid
)
834 return _error
->Error(_("Waited for %s but it wasn't there"),Name
);
838 // Check for an error code.
839 if (WIFEXITED(Status
) == 0 || WEXITSTATUS(Status
) != 0)
843 if (WIFSIGNALED(Status
) != 0)
845 if( WTERMSIG(Status
) == SIGSEGV
)
846 return _error
->Error(_("Sub-process %s received a segmentation fault."),Name
);
848 return _error
->Error(_("Sub-process %s received signal %u."),Name
, WTERMSIG(Status
));
851 if (WIFEXITED(Status
) != 0)
852 return _error
->Error(_("Sub-process %s returned an error code (%u)"),Name
,WEXITSTATUS(Status
));
854 return _error
->Error(_("Sub-process %s exited unexpectedly"),Name
);
860 // StartsWithGPGClearTextSignature - Check if a file is Pgp/GPG clearsigned /*{{{*/
861 bool StartsWithGPGClearTextSignature(string
const &FileName
)
863 static const char* SIGMSG
= "-----BEGIN PGP SIGNED MESSAGE-----\n";
864 char buffer
[strlen(SIGMSG
)+1];
865 FILE* gpg
= fopen(FileName
.c_str(), "r");
869 char const * const test
= fgets(buffer
, sizeof(buffer
), gpg
);
871 if (test
== NULL
|| strcmp(buffer
, SIGMSG
) != 0)
878 class FileFdPrivate
{ /*{{{*/
889 uint8_t buffer
[4096];
895 LZMAFILE() : file(NULL
), eof(false), compressing(false) { buffer
[0] = '\0'; }
897 if (compressing
== true)
900 stream
.avail_out
= sizeof(buffer
)/sizeof(buffer
[0]);
901 stream
.next_out
= buffer
;
902 err
= lzma_code(&stream
, LZMA_FINISH
);
903 if (err
!= LZMA_OK
&& err
!= LZMA_STREAM_END
)
905 _error
->Error("~LZMAFILE: Compress finalisation failed");
908 size_t const n
= sizeof(buffer
)/sizeof(buffer
[0]) - stream
.avail_out
;
909 if (n
&& fwrite(buffer
, 1, n
, file
) != n
)
911 _error
->Errno("~LZMAFILE",_("Write error"));
914 if (err
== LZMA_STREAM_END
)
925 pid_t compressor_pid
;
927 APT::Configuration::Compressor compressor
;
928 unsigned int openmode
;
929 unsigned long long seekpos
;
940 compressed_fd(-1), compressor_pid(-1), pipe(false),
941 openmode(0), seekpos(0) {};
942 bool InternalClose(std::string
const &FileName
)
945 /* dummy so that the rest can be 'else if's */;
947 else if (gz
!= NULL
) {
948 int const e
= gzclose(gz
);
950 // gzdclose() on empty files always fails with "buffer error" here, ignore that
951 if (e
!= 0 && e
!= Z_BUF_ERROR
)
952 return _error
->Errno("close",_("Problem closing the gzip file %s"), FileName
.c_str());
956 else if (bz2
!= NULL
) {
962 else if (lzma
!= NULL
) {
969 bool CloseDown(std::string
const &FileName
)
971 bool const Res
= InternalClose(FileName
);
973 if (compressor_pid
> 0)
974 ExecWait(compressor_pid
, "FileFdCompressor", true);
979 bool InternalStream() const {
991 ~FileFdPrivate() { CloseDown(""); }
994 // FileFd::Open - Open a file /*{{{*/
995 // ---------------------------------------------------------------------
996 /* The most commonly used open mode combinations are given with Mode */
997 bool FileFd::Open(string FileName
,unsigned int const Mode
,CompressMode Compress
, unsigned long const AccessMode
)
999 if (Mode
== ReadOnlyGzip
)
1000 return Open(FileName
, ReadOnly
, Gzip
, AccessMode
);
1002 if (Compress
== Auto
&& (Mode
& WriteOnly
) == WriteOnly
)
1003 return FileFdError("Autodetection on %s only works in ReadOnly openmode!", FileName
.c_str());
1005 std::vector
<APT::Configuration::Compressor
> const compressors
= APT::Configuration::getCompressors();
1006 std::vector
<APT::Configuration::Compressor
>::const_iterator compressor
= compressors
.begin();
1007 if (Compress
== Auto
)
1009 for (; compressor
!= compressors
.end(); ++compressor
)
1011 std::string file
= FileName
+ compressor
->Extension
;
1012 if (FileExists(file
) == false)
1018 else if (Compress
== Extension
)
1020 std::string::size_type
const found
= FileName
.find_last_of('.');
1022 if (found
!= std::string::npos
)
1024 ext
= FileName
.substr(found
);
1025 if (ext
== ".new" || ext
== ".bak")
1027 std::string::size_type
const found2
= FileName
.find_last_of('.', found
- 1);
1028 if (found2
!= std::string::npos
)
1029 ext
= FileName
.substr(found2
, found
- found2
);
1034 for (; compressor
!= compressors
.end(); ++compressor
)
1035 if (ext
== compressor
->Extension
)
1037 // no matching extension - assume uncompressed (imagine files like 'example.org_Packages')
1038 if (compressor
== compressors
.end())
1039 for (compressor
= compressors
.begin(); compressor
!= compressors
.end(); ++compressor
)
1040 if (compressor
->Name
== ".")
1048 case None
: name
= "."; break;
1049 case Gzip
: name
= "gzip"; break;
1050 case Bzip2
: name
= "bzip2"; break;
1051 case Lzma
: name
= "lzma"; break;
1052 case Xz
: name
= "xz"; break;
1056 return FileFdError("Opening File %s in None, Auto or Extension should be already handled?!?", FileName
.c_str());
1058 for (; compressor
!= compressors
.end(); ++compressor
)
1059 if (compressor
->Name
== name
)
1061 if (compressor
== compressors
.end())
1062 return FileFdError("Can't find a configured compressor %s for file %s", name
.c_str(), FileName
.c_str());
1065 if (compressor
== compressors
.end())
1066 return FileFdError("Can't find a match for specified compressor mode for file %s", FileName
.c_str());
1067 return Open(FileName
, Mode
, *compressor
, AccessMode
);
1069 bool FileFd::Open(string FileName
,unsigned int const Mode
,APT::Configuration::Compressor
const &compressor
, unsigned long const AccessMode
)
1074 if ((Mode
& WriteOnly
) != WriteOnly
&& (Mode
& (Atomic
| Create
| Empty
| Exclusive
)) != 0)
1075 return FileFdError("ReadOnly mode for %s doesn't accept additional flags!", FileName
.c_str());
1076 if ((Mode
& ReadWrite
) == 0)
1077 return FileFdError("No openmode provided in FileFd::Open for %s", FileName
.c_str());
1079 if ((Mode
& Atomic
) == Atomic
)
1083 else if ((Mode
& (Exclusive
| Create
)) == (Exclusive
| Create
))
1085 // for atomic, this will be done by rename in Close()
1086 unlink(FileName
.c_str());
1088 if ((Mode
& Empty
) == Empty
)
1091 if (lstat(FileName
.c_str(),&Buf
) == 0 && S_ISLNK(Buf
.st_mode
))
1092 unlink(FileName
.c_str());
1096 #define if_FLAGGED_SET(FLAG, MODE) if ((Mode & FLAG) == FLAG) fileflags |= MODE
1097 if_FLAGGED_SET(ReadWrite
, O_RDWR
);
1098 else if_FLAGGED_SET(ReadOnly
, O_RDONLY
);
1099 else if_FLAGGED_SET(WriteOnly
, O_WRONLY
);
1101 if_FLAGGED_SET(Create
, O_CREAT
);
1102 if_FLAGGED_SET(Empty
, O_TRUNC
);
1103 if_FLAGGED_SET(Exclusive
, O_EXCL
);
1104 #undef if_FLAGGED_SET
1106 if ((Mode
& Atomic
) == Atomic
)
1108 char *name
= strdup((FileName
+ ".XXXXXX").c_str());
1110 if((iFd
= mkstemp(name
)) == -1)
1113 return FileFdErrno("mkstemp", "Could not create temporary file for %s", FileName
.c_str());
1116 TemporaryFileName
= string(name
);
1119 // umask() will always set the umask and return the previous value, so
1120 // we first set the umask and then reset it to the old value
1121 mode_t
const CurrentUmask
= umask(0);
1122 umask(CurrentUmask
);
1123 // calculate the actual file permissions (just like open/creat)
1124 mode_t
const FilePermissions
= (AccessMode
& ~CurrentUmask
);
1126 if(fchmod(iFd
, FilePermissions
) == -1)
1127 return FileFdErrno("fchmod", "Could not change permissions for temporary file %s", TemporaryFileName
.c_str());
1130 iFd
= open(FileName
.c_str(), fileflags
, AccessMode
);
1132 this->FileName
= FileName
;
1133 if (iFd
== -1 || OpenInternDescriptor(Mode
, compressor
) == false)
1140 return FileFdErrno("open",_("Could not open file %s"), FileName
.c_str());
1143 SetCloseExec(iFd
,true);
1147 // FileFd::OpenDescriptor - Open a filedescriptor /*{{{*/
1148 // ---------------------------------------------------------------------
1150 bool FileFd::OpenDescriptor(int Fd
, unsigned int const Mode
, CompressMode Compress
, bool AutoClose
)
1152 std::vector
<APT::Configuration::Compressor
> const compressors
= APT::Configuration::getCompressors();
1153 std::vector
<APT::Configuration::Compressor
>::const_iterator compressor
= compressors
.begin();
1156 // compat with the old API
1157 if (Mode
== ReadOnlyGzip
&& Compress
== None
)
1162 case None
: name
= "."; break;
1163 case Gzip
: name
= "gzip"; break;
1164 case Bzip2
: name
= "bzip2"; break;
1165 case Lzma
: name
= "lzma"; break;
1166 case Xz
: name
= "xz"; break;
1169 if (AutoClose
== true && Fd
!= -1)
1171 return FileFdError("Opening Fd %d in Auto or Extension compression mode is not supported", Fd
);
1173 for (; compressor
!= compressors
.end(); ++compressor
)
1174 if (compressor
->Name
== name
)
1176 if (compressor
== compressors
.end())
1178 if (AutoClose
== true && Fd
!= -1)
1180 return FileFdError("Can't find a configured compressor %s for file %s", name
.c_str(), FileName
.c_str());
1182 return OpenDescriptor(Fd
, Mode
, *compressor
, AutoClose
);
1184 bool FileFd::OpenDescriptor(int Fd
, unsigned int const Mode
, APT::Configuration::Compressor
const &compressor
, bool AutoClose
)
1187 Flags
= (AutoClose
) ? FileFd::AutoClose
: 0;
1189 this->FileName
= "";
1190 if (OpenInternDescriptor(Mode
, compressor
) == false)
1193 (Flags
& Compressed
) == Compressed
||
1199 return FileFdError(_("Could not open file descriptor %d"), Fd
);
1203 bool FileFd::OpenInternDescriptor(unsigned int const Mode
, APT::Configuration::Compressor
const &compressor
)
1207 if (compressor
.Name
== "." || compressor
.Binary
.empty() == true)
1210 #if defined HAVE_ZLIB || defined HAVE_BZ2 || defined HAVE_LZMA
1211 // the API to open files is similar, so setup to avoid code duplicates later
1212 // and while at it ensure that we close before opening (if its a reopen)
1213 void* (*compress_open
)(int, const char *) = NULL
;
1215 /* dummy so that the rest can be 'else if's */;
1216 #define APT_COMPRESS_INIT(NAME,OPEN) \
1217 else if (compressor.Name == NAME) \
1219 compress_open = (void*(*)(int, const char *)) OPEN; \
1220 if (d != NULL) d->InternalClose(FileName); \
1223 APT_COMPRESS_INIT("gzip", gzdopen
)
1226 APT_COMPRESS_INIT("bzip2", BZ2_bzdopen
)
1229 APT_COMPRESS_INIT("xz", fdopen
)
1230 APT_COMPRESS_INIT("lzma", fdopen
)
1232 #undef APT_COMPRESS_INIT
1237 d
= new FileFdPrivate();
1239 d
->compressor
= compressor
;
1240 #if defined HAVE_ZLIB || defined HAVE_BZ2 || defined HAVE_LZMA
1241 if ((Flags
& AutoClose
) != AutoClose
&& compress_open
!= NULL
)
1243 // Need to duplicate fd here or gz/bz2 close for cleanup will close the fd as well
1244 int const internFd
= dup(iFd
);
1246 return FileFdErrno("OpenInternDescriptor", _("Could not open file descriptor %d"), iFd
);
1252 #if defined HAVE_ZLIB || defined HAVE_BZ2 || defined HAVE_LZMA
1253 if (compress_open
!= NULL
)
1255 void* compress_struct
= NULL
;
1256 if ((Mode
& ReadWrite
) == ReadWrite
)
1257 compress_struct
= compress_open(iFd
, "r+");
1258 else if ((Mode
& WriteOnly
) == WriteOnly
)
1259 compress_struct
= compress_open(iFd
, "w");
1261 compress_struct
= compress_open(iFd
, "r");
1262 if (compress_struct
== NULL
)
1266 /* dummy so that the rest can be 'else if's */;
1268 else if (compressor
.Name
== "gzip")
1269 d
->gz
= (gzFile
) compress_struct
;
1272 else if (compressor
.Name
== "bzip2")
1273 d
->bz2
= (BZFILE
*) compress_struct
;
1276 else if (compressor
.Name
== "xz" || compressor
.Name
== "lzma")
1278 uint32_t const xzlevel
= 6;
1279 uint64_t const memlimit
= UINT64_MAX
;
1280 if (d
->lzma
== NULL
)
1281 d
->lzma
= new FileFdPrivate::LZMAFILE
;
1282 d
->lzma
->file
= (FILE*) compress_struct
;
1283 lzma_stream tmp_stream
= LZMA_STREAM_INIT
;
1284 d
->lzma
->stream
= tmp_stream
;
1286 if ((Mode
& ReadWrite
) == ReadWrite
)
1287 return FileFdError("ReadWrite mode is not supported for file %s", FileName
.c_str());
1289 if ((Mode
& WriteOnly
) == WriteOnly
)
1291 if (compressor
.Name
== "xz")
1293 if (lzma_easy_encoder(&d
->lzma
->stream
, xzlevel
, LZMA_CHECK_CRC32
) != LZMA_OK
)
1298 lzma_options_lzma options
;
1299 lzma_lzma_preset(&options
, xzlevel
);
1300 if (lzma_alone_encoder(&d
->lzma
->stream
, &options
) != LZMA_OK
)
1303 d
->lzma
->compressing
= true;
1307 if (compressor
.Name
== "xz")
1309 if (lzma_auto_decoder(&d
->lzma
->stream
, memlimit
, 0) != LZMA_OK
)
1314 if (lzma_alone_decoder(&d
->lzma
->stream
, memlimit
) != LZMA_OK
)
1317 d
->lzma
->compressing
= false;
1321 Flags
|= Compressed
;
1326 // collect zombies here in case we reopen
1327 if (d
->compressor_pid
> 0)
1328 ExecWait(d
->compressor_pid
, "FileFdCompressor", true);
1330 if ((Mode
& ReadWrite
) == ReadWrite
)
1331 return FileFdError("ReadWrite mode is not supported for file %s", FileName
.c_str());
1333 bool const Comp
= (Mode
& WriteOnly
) == WriteOnly
;
1336 // Handle 'decompression' of empty files
1339 if (Buf
.st_size
== 0 && S_ISFIFO(Buf
.st_mode
) == false)
1342 // We don't need the file open - instead let the compressor open it
1343 // as he properly knows better how to efficiently read from 'his' file
1344 if (FileName
.empty() == false)
1351 // Create a data pipe
1352 int Pipe
[2] = {-1,-1};
1353 if (pipe(Pipe
) != 0)
1354 return FileFdErrno("pipe",_("Failed to create subprocess IPC"));
1355 for (int J
= 0; J
!= 2; J
++)
1356 SetCloseExec(Pipe
[J
],true);
1358 d
->compressed_fd
= iFd
;
1367 d
->compressor_pid
= ExecFork();
1368 if (d
->compressor_pid
== 0)
1372 dup2(d
->compressed_fd
,STDOUT_FILENO
);
1373 dup2(Pipe
[0],STDIN_FILENO
);
1377 if (d
->compressed_fd
!= -1)
1378 dup2(d
->compressed_fd
,STDIN_FILENO
);
1379 dup2(Pipe
[1],STDOUT_FILENO
);
1381 int const nullfd
= open("/dev/null", O_WRONLY
);
1384 dup2(nullfd
,STDERR_FILENO
);
1388 SetCloseExec(STDOUT_FILENO
,false);
1389 SetCloseExec(STDIN_FILENO
,false);
1391 std::vector
<char const*> Args
;
1392 Args
.push_back(compressor
.Binary
.c_str());
1393 std::vector
<std::string
> const * const addArgs
=
1394 (Comp
== true) ? &(compressor
.CompressArgs
) : &(compressor
.UncompressArgs
);
1395 for (std::vector
<std::string
>::const_iterator a
= addArgs
->begin();
1396 a
!= addArgs
->end(); ++a
)
1397 Args
.push_back(a
->c_str());
1398 if (Comp
== false && FileName
.empty() == false)
1400 // commands not needing arguments, do not need to be told about using standard output
1401 // in reality, only testcases with tools like cat, rev, rot13, … are able to trigger this
1402 if (compressor
.CompressArgs
.empty() == false && compressor
.UncompressArgs
.empty() == false)
1403 Args
.push_back("--stdout");
1404 if (TemporaryFileName
.empty() == false)
1405 Args
.push_back(TemporaryFileName
.c_str());
1407 Args
.push_back(FileName
.c_str());
1409 Args
.push_back(NULL
);
1411 execvp(Args
[0],(char **)&Args
[0]);
1412 cerr
<< _("Failed to exec compressor ") << Args
[0] << endl
;
1423 // FileFd::~File - Closes the file /*{{{*/
1424 // ---------------------------------------------------------------------
1425 /* If the proper modes are selected then we close the Fd and possibly
1426 unlink the file on error. */
1431 d
->CloseDown(FileName
);
1436 // FileFd::Read - Read a bit of the file /*{{{*/
1437 // ---------------------------------------------------------------------
1438 /* We are careful to handle interruption by a signal while reading
1440 bool FileFd::Read(void *To
,unsigned long long Size
,unsigned long long *Actual
)
1446 *((char *)To
) = '\0';
1450 /* dummy so that the rest can be 'else if's */;
1452 else if (d
!= NULL
&& d
->gz
!= NULL
)
1453 Res
= gzread(d
->gz
,To
,Size
);
1456 else if (d
!= NULL
&& d
->bz2
!= NULL
)
1457 Res
= BZ2_bzread(d
->bz2
,To
,Size
);
1460 else if (d
!= NULL
&& d
->lzma
!= NULL
)
1462 if (d
->lzma
->eof
== true)
1465 d
->lzma
->stream
.next_out
= (uint8_t *) To
;
1466 d
->lzma
->stream
.avail_out
= Size
;
1467 if (d
->lzma
->stream
.avail_in
== 0)
1469 d
->lzma
->stream
.next_in
= d
->lzma
->buffer
;
1470 d
->lzma
->stream
.avail_in
= fread(d
->lzma
->buffer
, 1, sizeof(d
->lzma
->buffer
)/sizeof(d
->lzma
->buffer
[0]), d
->lzma
->file
);
1472 d
->lzma
->err
= lzma_code(&d
->lzma
->stream
, LZMA_RUN
);
1473 if (d
->lzma
->err
== LZMA_STREAM_END
)
1475 d
->lzma
->eof
= true;
1476 Res
= Size
- d
->lzma
->stream
.avail_out
;
1478 else if (d
->lzma
->err
!= LZMA_OK
)
1485 Res
= Size
- d
->lzma
->stream
.avail_out
;
1488 // lzma run was okay, but produced no output…
1496 Res
= read(iFd
,To
,Size
);
1502 // trick the while-loop into running again
1508 /* dummy so that the rest can be 'else if's */;
1510 else if (d
!= NULL
&& d
->gz
!= NULL
)
1513 char const * const errmsg
= gzerror(d
->gz
, &err
);
1515 return FileFdError("gzread: %s (%d: %s)", _("Read error"), err
, errmsg
);
1519 else if (d
!= NULL
&& d
->bz2
!= NULL
)
1522 char const * const errmsg
= BZ2_bzerror(d
->bz2
, &err
);
1523 if (err
!= BZ_IO_ERROR
)
1524 return FileFdError("BZ2_bzread: %s (%d: %s)", _("Read error"), err
, errmsg
);
1528 else if (d
!= NULL
&& d
->lzma
!= NULL
)
1529 return FileFdError("lzma_read: %s (%d)", _("Read error"), d
->lzma
->err
);
1531 return FileFdErrno("read",_("Read error"));
1534 To
= (char *)To
+ Res
;
1541 while (Res
> 0 && Size
> 0);
1553 return FileFdError(_("read, still have %llu to read but none left"), Size
);
1556 // FileFd::ReadLine - Read a complete line from the file /*{{{*/
1557 // ---------------------------------------------------------------------
1558 /* Beware: This method can be quiet slow for big buffers on UNcompressed
1559 files because of the naive implementation! */
1560 char* FileFd::ReadLine(char *To
, unsigned long long const Size
)
1564 if (d
!= NULL
&& d
->gz
!= NULL
)
1565 return gzgets(d
->gz
, To
, Size
);
1568 unsigned long long read
= 0;
1569 while ((Size
- 1) != read
)
1571 unsigned long long done
= 0;
1572 if (Read(To
+ read
, 1, &done
) == false)
1576 if (To
[read
++] == '\n')
1585 // FileFd::Write - Write to the file /*{{{*/
1586 // ---------------------------------------------------------------------
1588 bool FileFd::Write(const void *From
,unsigned long long Size
)
1595 /* dummy so that the rest can be 'else if's */;
1597 else if (d
!= NULL
&& d
->gz
!= NULL
)
1598 Res
= gzwrite(d
->gz
,From
,Size
);
1601 else if (d
!= NULL
&& d
->bz2
!= NULL
)
1602 Res
= BZ2_bzwrite(d
->bz2
,(void*)From
,Size
);
1605 else if (d
!= NULL
&& d
->lzma
!= NULL
)
1607 d
->lzma
->stream
.next_in
= (uint8_t *)From
;
1608 d
->lzma
->stream
.avail_in
= Size
;
1609 d
->lzma
->stream
.next_out
= d
->lzma
->buffer
;
1610 d
->lzma
->stream
.avail_out
= sizeof(d
->lzma
->buffer
)/sizeof(d
->lzma
->buffer
[0]);
1611 d
->lzma
->err
= lzma_code(&d
->lzma
->stream
, LZMA_RUN
);
1612 if (d
->lzma
->err
!= LZMA_OK
)
1614 size_t const n
= sizeof(d
->lzma
->buffer
)/sizeof(d
->lzma
->buffer
[0]) - d
->lzma
->stream
.avail_out
;
1615 size_t const m
= (n
== 0) ? 0 : fwrite(d
->lzma
->buffer
, 1, n
, d
->lzma
->file
);
1619 Res
= Size
- d
->lzma
->stream
.avail_in
;
1623 Res
= write(iFd
,From
,Size
);
1625 if (Res
< 0 && errno
== EINTR
)
1630 /* dummy so that the rest can be 'else if's */;
1632 else if (d
!= NULL
&& d
->gz
!= NULL
)
1635 char const * const errmsg
= gzerror(d
->gz
, &err
);
1637 return FileFdError("gzwrite: %s (%d: %s)", _("Write error"), err
, errmsg
);
1641 else if (d
!= NULL
&& d
->bz2
!= NULL
)
1644 char const * const errmsg
= BZ2_bzerror(d
->bz2
, &err
);
1645 if (err
!= BZ_IO_ERROR
)
1646 return FileFdError("BZ2_bzwrite: %s (%d: %s)", _("Write error"), err
, errmsg
);
1650 else if (d
!= NULL
&& d
->lzma
!= NULL
)
1651 return FileFdErrno("lzma_fwrite", _("Write error"));
1653 return FileFdErrno("write",_("Write error"));
1656 From
= (char const *)From
+ Res
;
1661 while (Res
> 0 && Size
> 0);
1666 return FileFdError(_("write, still have %llu to write but couldn't"), Size
);
1668 bool FileFd::Write(int Fd
, const void *From
, unsigned long long Size
)
1674 Res
= write(Fd
,From
,Size
);
1675 if (Res
< 0 && errno
== EINTR
)
1678 return _error
->Errno("write",_("Write error"));
1680 From
= (char const *)From
+ Res
;
1683 while (Res
> 0 && Size
> 0);
1688 return _error
->Error(_("write, still have %llu to write but couldn't"), Size
);
1691 // FileFd::Seek - Seek in the file /*{{{*/
1692 // ---------------------------------------------------------------------
1694 bool FileFd::Seek(unsigned long long To
)
1698 if (d
!= NULL
&& (d
->pipe
== true || d
->InternalStream() == true))
1700 // Our poor man seeking in pipes is costly, so try to avoid it
1701 unsigned long long seekpos
= Tell();
1704 else if (seekpos
< To
)
1705 return Skip(To
- seekpos
);
1707 if ((d
->openmode
& ReadOnly
) != ReadOnly
)
1708 return FileFdError("Reopen is only implemented for read-only files!");
1709 d
->InternalClose(FileName
);
1713 if (TemporaryFileName
.empty() == false)
1714 iFd
= open(TemporaryFileName
.c_str(), O_RDONLY
);
1715 else if (FileName
.empty() == false)
1716 iFd
= open(FileName
.c_str(), O_RDONLY
);
1719 if (d
->compressed_fd
> 0)
1720 if (lseek(d
->compressed_fd
, 0, SEEK_SET
) != 0)
1721 iFd
= d
->compressed_fd
;
1723 return FileFdError("Reopen is not implemented for pipes opened with FileFd::OpenDescriptor()!");
1726 if (OpenInternDescriptor(d
->openmode
, d
->compressor
) == false)
1727 return FileFdError("Seek on file %s because it couldn't be reopened", FileName
.c_str());
1737 if (d
!= NULL
&& d
->gz
)
1738 res
= gzseek(d
->gz
,To
,SEEK_SET
);
1741 res
= lseek(iFd
,To
,SEEK_SET
);
1742 if (res
!= (off_t
)To
)
1743 return FileFdError("Unable to seek to %llu", To
);
1750 // FileFd::Skip - Seek in the file /*{{{*/
1751 // ---------------------------------------------------------------------
1753 bool FileFd::Skip(unsigned long long Over
)
1755 if (d
!= NULL
&& (d
->pipe
== true || d
->InternalStream() == true))
1760 unsigned long long toread
= std::min((unsigned long long) sizeof(buffer
), Over
);
1761 if (Read(buffer
, toread
) == false)
1762 return FileFdError("Unable to seek ahead %llu",Over
);
1770 if (d
!= NULL
&& d
->gz
!= NULL
)
1771 res
= gzseek(d
->gz
,Over
,SEEK_CUR
);
1774 res
= lseek(iFd
,Over
,SEEK_CUR
);
1776 return FileFdError("Unable to seek ahead %llu",Over
);
1783 // FileFd::Truncate - Truncate the file /*{{{*/
1784 // ---------------------------------------------------------------------
1786 bool FileFd::Truncate(unsigned long long To
)
1788 // truncating /dev/null is always successful - as we get an error otherwise
1789 if (To
== 0 && FileName
== "/dev/null")
1791 #if defined HAVE_ZLIB || defined HAVE_BZ2 || defined HAVE_LZMA
1792 if (d
!= NULL
&& (d
->InternalStream() == true
1797 return FileFdError("Truncating compressed files is not implemented (%s)", FileName
.c_str());
1799 if (ftruncate(iFd
,To
) != 0)
1800 return FileFdError("Unable to truncate to %llu",To
);
1805 // FileFd::Tell - Current seek position /*{{{*/
1806 // ---------------------------------------------------------------------
1808 unsigned long long FileFd::Tell()
1810 // In theory, we could just return seekpos here always instead of
1811 // seeking around, but not all users of FileFd use always Seek() and co
1812 // so d->seekpos isn't always true and we can just use it as a hint if
1813 // we have nothing else, but not always as an authority…
1814 if (d
!= NULL
&& (d
->pipe
== true || d
->InternalStream() == true))
1819 if (d
!= NULL
&& d
->gz
!= NULL
)
1820 Res
= gztell(d
->gz
);
1823 Res
= lseek(iFd
,0,SEEK_CUR
);
1824 if (Res
== (off_t
)-1)
1825 FileFdErrno("lseek","Failed to determine the current file position");
1831 static bool StatFileFd(char const * const msg
, int const iFd
, std::string
const &FileName
, struct stat
&Buf
, FileFdPrivate
* const d
) /*{{{*/
1833 bool ispipe
= (d
!= NULL
&& d
->pipe
== true);
1834 if (ispipe
== false)
1836 if (fstat(iFd
,&Buf
) != 0)
1837 // higher-level code will generate more meaningful messages,
1838 // even translated this would be meaningless for users
1839 return _error
->Errno("fstat", "Unable to determine %s for fd %i", msg
, iFd
);
1840 if (FileName
.empty() == false)
1841 ispipe
= S_ISFIFO(Buf
.st_mode
);
1844 // for compressor pipes st_size is undefined and at 'best' zero
1847 // we set it here, too, as we get the info here for free
1848 // in theory the Open-methods should take care of it already
1851 if (stat(FileName
.c_str(), &Buf
) != 0)
1852 return _error
->Errno("fstat", "Unable to determine %s for file %s", msg
, FileName
.c_str());
1857 // FileFd::FileSize - Return the size of the file /*{{{*/
1858 unsigned long long FileFd::FileSize()
1861 if (StatFileFd("file size", iFd
, FileName
, Buf
, d
) == false)
1869 // FileFd::ModificationTime - Return the time of last touch /*{{{*/
1870 time_t FileFd::ModificationTime()
1873 if (StatFileFd("modification time", iFd
, FileName
, Buf
, d
) == false)
1878 return Buf
.st_mtime
;
1881 // FileFd::Size - Return the size of the content in the file /*{{{*/
1882 // ---------------------------------------------------------------------
1884 unsigned long long FileFd::Size()
1886 unsigned long long size
= FileSize();
1888 // for compressor pipes st_size is undefined and at 'best' zero,
1889 // so we 'read' the content and 'seek' back - see there
1890 if (d
!= NULL
&& (d
->pipe
== true || (d
->InternalStream() == true && size
> 0)))
1892 unsigned long long const oldSeek
= Tell();
1894 unsigned long long read
= 0;
1896 if (Read(ignore
, sizeof(ignore
), &read
) == false)
1906 // only check gzsize if we are actually a gzip file, just checking for
1907 // "gz" is not sufficient as uncompressed files could be opened with
1908 // gzopen in "direct" mode as well
1909 else if (d
!= NULL
&& d
->gz
&& !gzdirect(d
->gz
) && size
> 0)
1911 off_t
const oldPos
= lseek(iFd
,0,SEEK_CUR
);
1912 /* unfortunately zlib.h doesn't provide a gzsize(), so we have to do
1913 * this ourselves; the original (uncompressed) file size is the last 32
1914 * bits of the file */
1915 // FIXME: Size for gz-files is limited by 32bit… no largefile support
1916 if (lseek(iFd
, -4, SEEK_END
) < 0)
1918 FileFdErrno("lseek","Unable to seek to end of gzipped file");
1922 if (read(iFd
, &size
, 4) != 4)
1924 FileFdErrno("read","Unable to read original size of gzipped file");
1927 size
= le32toh(size
);
1929 if (lseek(iFd
, oldPos
, SEEK_SET
) < 0)
1931 FileFdErrno("lseek","Unable to seek in gzipped file");
1942 // FileFd::Close - Close the file if the close flag is set /*{{{*/
1943 // ---------------------------------------------------------------------
1945 bool FileFd::Close()
1951 if ((Flags
& AutoClose
) == AutoClose
)
1953 if ((Flags
& Compressed
) != Compressed
&& iFd
> 0 && close(iFd
) != 0)
1954 Res
&= _error
->Errno("close",_("Problem closing the file %s"), FileName
.c_str());
1957 Res
&= d
->CloseDown(FileName
);
1963 if ((Flags
& Replace
) == Replace
) {
1964 if (rename(TemporaryFileName
.c_str(), FileName
.c_str()) != 0)
1965 Res
&= _error
->Errno("rename",_("Problem renaming the file %s to %s"), TemporaryFileName
.c_str(), FileName
.c_str());
1967 FileName
= TemporaryFileName
; // for the unlink() below.
1968 TemporaryFileName
.clear();
1973 if ((Flags
& Fail
) == Fail
&& (Flags
& DelOnFail
) == DelOnFail
&&
1974 FileName
.empty() == false)
1975 if (unlink(FileName
.c_str()) != 0)
1976 Res
&= _error
->WarningE("unlnk",_("Problem unlinking the file %s"), FileName
.c_str());
1983 // FileFd::Sync - Sync the file /*{{{*/
1984 // ---------------------------------------------------------------------
1988 if (fsync(iFd
) != 0)
1989 return FileFdErrno("sync",_("Problem syncing the file"));
1993 // FileFd::FileFdErrno - set Fail and call _error->Errno *{{{*/
1994 bool FileFd::FileFdErrno(const char *Function
, const char *Description
,...)
1998 size_t msgSize
= 400;
1999 int const errsv
= errno
;
2002 va_start(args
,Description
);
2003 if (_error
->InsertErrno(GlobalError::ERROR
, Function
, Description
, args
, errsv
, msgSize
) == false)
2010 // FileFd::FileFdError - set Fail and call _error->Error *{{{*/
2011 bool FileFd::FileFdError(const char *Description
,...) {
2014 size_t msgSize
= 400;
2017 va_start(args
,Description
);
2018 if (_error
->Insert(GlobalError::ERROR
, Description
, args
, msgSize
) == false)
2026 APT_DEPRECATED gzFile
FileFd::gzFd() {
2034 // Glob - wrapper around "glob()" /*{{{*/
2035 std::vector
<std::string
> Glob(std::string
const &pattern
, int flags
)
2037 std::vector
<std::string
> result
;
2042 glob_res
= glob(pattern
.c_str(), flags
, NULL
, &globbuf
);
2046 if(glob_res
!= GLOB_NOMATCH
) {
2047 _error
->Errno("glob", "Problem with glob");
2053 for(i
=0;i
<globbuf
.gl_pathc
;i
++)
2054 result
.push_back(string(globbuf
.gl_pathv
[i
]));
2060 std::string
GetTempDir() /*{{{*/
2062 const char *tmpdir
= getenv("TMPDIR");
2069 // check that tmpdir is set and exists
2071 if (!tmpdir
|| strlen(tmpdir
) == 0 || stat(tmpdir
, &st
) != 0)
2074 return string(tmpdir
);
2077 FileFd
* GetTempFile(std::string
const &Prefix
, bool ImmediateUnlink
) /*{{{*/
2080 FileFd
*Fd
= new FileFd();
2082 std::string tempdir
= GetTempDir();
2083 snprintf(fn
, sizeof(fn
), "%s/%s.XXXXXX",
2084 tempdir
.c_str(), Prefix
.c_str());
2085 int fd
= mkstemp(fn
);
2090 _error
->Errno("GetTempFile",_("Unable to mkstemp %s"), fn
);
2093 if (!Fd
->OpenDescriptor(fd
, FileFd::WriteOnly
, FileFd::None
, true))
2095 _error
->Errno("GetTempFile",_("Unable to write to %s"),fn
);
2102 bool Rename(std::string From
, std::string To
) /*{{{*/
2104 if (rename(From
.c_str(),To
.c_str()) != 0)
2106 _error
->Error(_("rename failed, %s (%s -> %s)."),strerror(errno
),
2107 From
.c_str(),To
.c_str());
2113 bool Popen(const char* Args
[], FileFd
&Fd
, pid_t
&Child
, FileFd::OpenMode Mode
)/*{{{*/
2116 if (Mode
!= FileFd::ReadOnly
&& Mode
!= FileFd::WriteOnly
)
2117 return _error
->Error("Popen supports ReadOnly (x)or WriteOnly mode only");
2119 int Pipe
[2] = {-1, -1};
2121 return _error
->Errno("pipe", _("Failed to create subprocess IPC"));
2123 std::set
<int> keep_fds
;
2124 keep_fds
.insert(Pipe
[0]);
2125 keep_fds
.insert(Pipe
[1]);
2126 Child
= ExecFork(keep_fds
);
2128 return _error
->Errno("fork", "Failed to fork");
2131 if(Mode
== FileFd::ReadOnly
)
2136 else if(Mode
== FileFd::WriteOnly
)
2142 if(Mode
== FileFd::ReadOnly
)
2146 } else if(Mode
== FileFd::WriteOnly
)
2149 execv(Args
[0], (char**)Args
);
2152 if(Mode
== FileFd::ReadOnly
)
2156 } else if(Mode
== FileFd::WriteOnly
)
2161 Fd
.OpenDescriptor(fd
, Mode
, FileFd::None
, true);
2166 bool DropPrivileges() /*{{{*/
2168 // uid will be 0 in the end, but gid might be different anyway
2169 uid_t old_uid
= getuid();
2170 gid_t old_gid
= getgid();
2174 if(_config
->FindB("Debug::NoDropPrivs", false) == true)
2177 const std::string toUser
= _config
->Find("APT::Sandbox::User", "_apt");
2178 struct passwd
*pw
= getpwnam(toUser
.c_str());
2180 return _error
->Error("No user %s, can not drop rights", toUser
.c_str());
2183 // see prctl(2), needs linux3.5
2184 int ret
= prctl(PR_SET_NO_NEW_PRIVS
, 1, 0, 0, 0);
2185 // ignore EINVAL - kernel is too old to understand the option
2186 if(ret
< 0 && errno
!= EINVAL
)
2187 _error
->Warning("PR_SET_NO_NEW_PRIVS failed with %i", ret
);
2189 // Do not change the order here, it might break things
2190 if (setgroups(1, &pw
->pw_gid
))
2191 return _error
->Errno("setgroups", "Failed to setgroups");
2193 if (setegid(pw
->pw_gid
) != 0)
2194 return _error
->Errno("setegid", "Failed to setegid");
2196 if (setgid(pw
->pw_gid
) != 0)
2197 return _error
->Errno("setgid", "Failed to setgid");
2199 if (setuid(pw
->pw_uid
) != 0)
2200 return _error
->Errno("setuid", "Failed to setuid");
2202 // the seteuid() is probably uneeded (at least thats what the linux
2203 // man-page says about setuid(2)) but we cargo culted it anyway
2204 if (seteuid(pw
->pw_uid
) != 0)
2205 return _error
->Errno("seteuid", "Failed to seteuid");
2207 // Verify that the user has only a single group, and the correct one
2209 if (getgroups(1, groups
) != 1)
2210 return _error
->Errno("getgroups", "Could not get new groups");
2211 if (groups
[0] != pw
->pw_gid
)
2212 return _error
->Error("Could not switch group");
2214 // Verify that gid, egid, uid, and euid changed
2215 if (getgid() != pw
->pw_gid
)
2216 return _error
->Error("Could not switch group");
2217 if (getegid() != pw
->pw_gid
)
2218 return _error
->Error("Could not switch effective group");
2219 if (getuid() != pw
->pw_uid
)
2220 return _error
->Error("Could not switch user");
2221 if (geteuid() != pw
->pw_uid
)
2222 return _error
->Error("Could not switch effective user");
2224 #ifdef HAVE_GETRESUID
2225 // verify that the saved set-user-id was changed as well
2229 if (getresuid(&ruid
, &euid
, &suid
))
2230 return _error
->Errno("getresuid", "Could not get saved set-user-ID");
2231 if (suid
!= pw
->pw_uid
)
2232 return _error
->Error("Could not switch saved set-user-ID");
2235 #ifdef HAVE_GETRESGID
2236 // verify that the saved set-group-id was changed as well
2240 if (getresgid(&rgid
, &egid
, &sgid
))
2241 return _error
->Errno("getresuid", "Could not get saved set-group-ID");
2242 if (sgid
!= pw
->pw_gid
)
2243 return _error
->Error("Could not switch saved set-group-ID");
2246 // Check that uid and gid changes do not work anymore
2247 if (pw
->pw_gid
!= old_gid
&& (setgid(old_gid
) != -1 || setegid(old_gid
) != -1))
2248 return _error
->Error("Could restore a gid to root, privilege dropping did not work");
2250 if (pw
->pw_uid
!= old_uid
&& (setuid(old_uid
) != -1 || seteuid(old_uid
) != -1))
2251 return _error
->Error("Could restore a uid to root, privilege dropping did not work");