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 long ScOpenMax
= sysconf(_SC_OPEN_MAX
);
804 // Close all of our FDs - just in case
805 for (int K
= 3; K
!= ScOpenMax
; K
++)
807 if(KeepFDs
.find(K
) == KeepFDs
.end())
808 fcntl(K
,F_SETFD
,FD_CLOEXEC
);
815 // ExecWait - Fancy waitpid /*{{{*/
816 // ---------------------------------------------------------------------
817 /* Waits for the given sub process. If Reap is set then no errors are
818 generated. Otherwise a failed subprocess will generate a proper descriptive
820 bool ExecWait(pid_t Pid
,const char *Name
,bool Reap
)
825 // Wait and collect the error code
827 while (waitpid(Pid
,&Status
,0) != Pid
)
835 return _error
->Error(_("Waited for %s but it wasn't there"),Name
);
839 // Check for an error code.
840 if (WIFEXITED(Status
) == 0 || WEXITSTATUS(Status
) != 0)
844 if (WIFSIGNALED(Status
) != 0)
846 if( WTERMSIG(Status
) == SIGSEGV
)
847 return _error
->Error(_("Sub-process %s received a segmentation fault."),Name
);
849 return _error
->Error(_("Sub-process %s received signal %u."),Name
, WTERMSIG(Status
));
852 if (WIFEXITED(Status
) != 0)
853 return _error
->Error(_("Sub-process %s returned an error code (%u)"),Name
,WEXITSTATUS(Status
));
855 return _error
->Error(_("Sub-process %s exited unexpectedly"),Name
);
861 // StartsWithGPGClearTextSignature - Check if a file is Pgp/GPG clearsigned /*{{{*/
862 bool StartsWithGPGClearTextSignature(string
const &FileName
)
864 static const char* SIGMSG
= "-----BEGIN PGP SIGNED MESSAGE-----\n";
865 char buffer
[strlen(SIGMSG
)+1];
866 FILE* gpg
= fopen(FileName
.c_str(), "r");
870 char const * const test
= fgets(buffer
, sizeof(buffer
), gpg
);
872 if (test
== NULL
|| strcmp(buffer
, SIGMSG
) != 0)
878 // ChangeOwnerAndPermissionOfFile - set file attributes to requested values /*{{{*/
879 bool ChangeOwnerAndPermissionOfFile(char const * const requester
, char const * const file
, char const * const user
, char const * const group
, mode_t
const mode
)
881 if (strcmp(file
, "/dev/null") == 0)
884 if (getuid() == 0 && strlen(user
) != 0 && strlen(group
) != 0) // if we aren't root, we can't chown, so don't try it
886 // ensure the file is owned by root and has good permissions
887 struct passwd
const * const pw
= getpwnam(user
);
888 struct group
const * const gr
= getgrnam(group
);
889 if (pw
!= NULL
&& gr
!= NULL
&& chown(file
, pw
->pw_uid
, gr
->gr_gid
) != 0)
890 Res
&= _error
->WarningE(requester
, "chown to %s:%s of file %s failed", user
, group
, file
);
892 if (chmod(file
, mode
) != 0)
893 Res
&= _error
->WarningE(requester
, "chmod 0%o of file %s failed", mode
, file
);
898 class FileFdPrivate
{ /*{{{*/
909 uint8_t buffer
[4096];
915 LZMAFILE() : file(NULL
), eof(false), compressing(false) { buffer
[0] = '\0'; }
917 if (compressing
== true)
920 stream
.avail_out
= sizeof(buffer
)/sizeof(buffer
[0]);
921 stream
.next_out
= buffer
;
922 err
= lzma_code(&stream
, LZMA_FINISH
);
923 if (err
!= LZMA_OK
&& err
!= LZMA_STREAM_END
)
925 _error
->Error("~LZMAFILE: Compress finalisation failed");
928 size_t const n
= sizeof(buffer
)/sizeof(buffer
[0]) - stream
.avail_out
;
929 if (n
&& fwrite(buffer
, 1, n
, file
) != n
)
931 _error
->Errno("~LZMAFILE",_("Write error"));
934 if (err
== LZMA_STREAM_END
)
945 pid_t compressor_pid
;
947 APT::Configuration::Compressor compressor
;
948 unsigned int openmode
;
949 unsigned long long seekpos
;
960 compressed_fd(-1), compressor_pid(-1), pipe(false),
961 openmode(0), seekpos(0) {};
962 bool InternalClose(std::string
const &FileName
)
965 /* dummy so that the rest can be 'else if's */;
967 else if (gz
!= NULL
) {
968 int const e
= gzclose(gz
);
970 // gzdclose() on empty files always fails with "buffer error" here, ignore that
971 if (e
!= 0 && e
!= Z_BUF_ERROR
)
972 return _error
->Errno("close",_("Problem closing the gzip file %s"), FileName
.c_str());
976 else if (bz2
!= NULL
) {
982 else if (lzma
!= NULL
) {
989 bool CloseDown(std::string
const &FileName
)
991 bool const Res
= InternalClose(FileName
);
993 if (compressor_pid
> 0)
994 ExecWait(compressor_pid
, "FileFdCompressor", true);
999 bool InternalStream() const {
1011 ~FileFdPrivate() { CloseDown(""); }
1014 // FileFd::Open - Open a file /*{{{*/
1015 // ---------------------------------------------------------------------
1016 /* The most commonly used open mode combinations are given with Mode */
1017 bool FileFd::Open(string FileName
,unsigned int const Mode
,CompressMode Compress
, unsigned long const AccessMode
)
1019 if (Mode
== ReadOnlyGzip
)
1020 return Open(FileName
, ReadOnly
, Gzip
, AccessMode
);
1022 if (Compress
== Auto
&& (Mode
& WriteOnly
) == WriteOnly
)
1023 return FileFdError("Autodetection on %s only works in ReadOnly openmode!", FileName
.c_str());
1025 std::vector
<APT::Configuration::Compressor
> const compressors
= APT::Configuration::getCompressors();
1026 std::vector
<APT::Configuration::Compressor
>::const_iterator compressor
= compressors
.begin();
1027 if (Compress
== Auto
)
1029 for (; compressor
!= compressors
.end(); ++compressor
)
1031 std::string file
= FileName
+ compressor
->Extension
;
1032 if (FileExists(file
) == false)
1038 else if (Compress
== Extension
)
1040 std::string::size_type
const found
= FileName
.find_last_of('.');
1042 if (found
!= std::string::npos
)
1044 ext
= FileName
.substr(found
);
1045 if (ext
== ".new" || ext
== ".bak")
1047 std::string::size_type
const found2
= FileName
.find_last_of('.', found
- 1);
1048 if (found2
!= std::string::npos
)
1049 ext
= FileName
.substr(found2
, found
- found2
);
1054 for (; compressor
!= compressors
.end(); ++compressor
)
1055 if (ext
== compressor
->Extension
)
1057 // no matching extension - assume uncompressed (imagine files like 'example.org_Packages')
1058 if (compressor
== compressors
.end())
1059 for (compressor
= compressors
.begin(); compressor
!= compressors
.end(); ++compressor
)
1060 if (compressor
->Name
== ".")
1068 case None
: name
= "."; break;
1069 case Gzip
: name
= "gzip"; break;
1070 case Bzip2
: name
= "bzip2"; break;
1071 case Lzma
: name
= "lzma"; break;
1072 case Xz
: name
= "xz"; break;
1076 return FileFdError("Opening File %s in None, Auto or Extension should be already handled?!?", FileName
.c_str());
1078 for (; compressor
!= compressors
.end(); ++compressor
)
1079 if (compressor
->Name
== name
)
1081 if (compressor
== compressors
.end())
1082 return FileFdError("Can't find a configured compressor %s for file %s", name
.c_str(), FileName
.c_str());
1085 if (compressor
== compressors
.end())
1086 return FileFdError("Can't find a match for specified compressor mode for file %s", FileName
.c_str());
1087 return Open(FileName
, Mode
, *compressor
, AccessMode
);
1089 bool FileFd::Open(string FileName
,unsigned int const Mode
,APT::Configuration::Compressor
const &compressor
, unsigned long const AccessMode
)
1094 if ((Mode
& WriteOnly
) != WriteOnly
&& (Mode
& (Atomic
| Create
| Empty
| Exclusive
)) != 0)
1095 return FileFdError("ReadOnly mode for %s doesn't accept additional flags!", FileName
.c_str());
1096 if ((Mode
& ReadWrite
) == 0)
1097 return FileFdError("No openmode provided in FileFd::Open for %s", FileName
.c_str());
1099 if ((Mode
& Atomic
) == Atomic
)
1103 else if ((Mode
& (Exclusive
| Create
)) == (Exclusive
| Create
))
1105 // for atomic, this will be done by rename in Close()
1106 unlink(FileName
.c_str());
1108 if ((Mode
& Empty
) == Empty
)
1111 if (lstat(FileName
.c_str(),&Buf
) == 0 && S_ISLNK(Buf
.st_mode
))
1112 unlink(FileName
.c_str());
1116 #define if_FLAGGED_SET(FLAG, MODE) if ((Mode & FLAG) == FLAG) fileflags |= MODE
1117 if_FLAGGED_SET(ReadWrite
, O_RDWR
);
1118 else if_FLAGGED_SET(ReadOnly
, O_RDONLY
);
1119 else if_FLAGGED_SET(WriteOnly
, O_WRONLY
);
1121 if_FLAGGED_SET(Create
, O_CREAT
);
1122 if_FLAGGED_SET(Empty
, O_TRUNC
);
1123 if_FLAGGED_SET(Exclusive
, O_EXCL
);
1124 #undef if_FLAGGED_SET
1126 if ((Mode
& Atomic
) == Atomic
)
1128 char *name
= strdup((FileName
+ ".XXXXXX").c_str());
1130 if((iFd
= mkstemp(name
)) == -1)
1133 return FileFdErrno("mkstemp", "Could not create temporary file for %s", FileName
.c_str());
1136 TemporaryFileName
= string(name
);
1139 // umask() will always set the umask and return the previous value, so
1140 // we first set the umask and then reset it to the old value
1141 mode_t
const CurrentUmask
= umask(0);
1142 umask(CurrentUmask
);
1143 // calculate the actual file permissions (just like open/creat)
1144 mode_t
const FilePermissions
= (AccessMode
& ~CurrentUmask
);
1146 if(fchmod(iFd
, FilePermissions
) == -1)
1147 return FileFdErrno("fchmod", "Could not change permissions for temporary file %s", TemporaryFileName
.c_str());
1150 iFd
= open(FileName
.c_str(), fileflags
, AccessMode
);
1152 this->FileName
= FileName
;
1153 if (iFd
== -1 || OpenInternDescriptor(Mode
, compressor
) == false)
1160 return FileFdErrno("open",_("Could not open file %s"), FileName
.c_str());
1163 SetCloseExec(iFd
,true);
1167 // FileFd::OpenDescriptor - Open a filedescriptor /*{{{*/
1168 // ---------------------------------------------------------------------
1170 bool FileFd::OpenDescriptor(int Fd
, unsigned int const Mode
, CompressMode Compress
, bool AutoClose
)
1172 std::vector
<APT::Configuration::Compressor
> const compressors
= APT::Configuration::getCompressors();
1173 std::vector
<APT::Configuration::Compressor
>::const_iterator compressor
= compressors
.begin();
1176 // compat with the old API
1177 if (Mode
== ReadOnlyGzip
&& Compress
== None
)
1182 case None
: name
= "."; break;
1183 case Gzip
: name
= "gzip"; break;
1184 case Bzip2
: name
= "bzip2"; break;
1185 case Lzma
: name
= "lzma"; break;
1186 case Xz
: name
= "xz"; break;
1189 if (AutoClose
== true && Fd
!= -1)
1191 return FileFdError("Opening Fd %d in Auto or Extension compression mode is not supported", Fd
);
1193 for (; compressor
!= compressors
.end(); ++compressor
)
1194 if (compressor
->Name
== name
)
1196 if (compressor
== compressors
.end())
1198 if (AutoClose
== true && Fd
!= -1)
1200 return FileFdError("Can't find a configured compressor %s for file %s", name
.c_str(), FileName
.c_str());
1202 return OpenDescriptor(Fd
, Mode
, *compressor
, AutoClose
);
1204 bool FileFd::OpenDescriptor(int Fd
, unsigned int const Mode
, APT::Configuration::Compressor
const &compressor
, bool AutoClose
)
1207 Flags
= (AutoClose
) ? FileFd::AutoClose
: 0;
1209 this->FileName
= "";
1210 if (OpenInternDescriptor(Mode
, compressor
) == false)
1213 (Flags
& Compressed
) == Compressed
||
1219 return FileFdError(_("Could not open file descriptor %d"), Fd
);
1223 bool FileFd::OpenInternDescriptor(unsigned int const Mode
, APT::Configuration::Compressor
const &compressor
)
1227 if (compressor
.Name
== "." || compressor
.Binary
.empty() == true)
1230 #if defined HAVE_ZLIB || defined HAVE_BZ2 || defined HAVE_LZMA
1231 // the API to open files is similar, so setup to avoid code duplicates later
1232 // and while at it ensure that we close before opening (if its a reopen)
1233 void* (*compress_open
)(int, const char *) = NULL
;
1235 /* dummy so that the rest can be 'else if's */;
1236 #define APT_COMPRESS_INIT(NAME,OPEN) \
1237 else if (compressor.Name == NAME) \
1239 compress_open = (void*(*)(int, const char *)) OPEN; \
1240 if (d != NULL) d->InternalClose(FileName); \
1243 APT_COMPRESS_INIT("gzip", gzdopen
)
1246 APT_COMPRESS_INIT("bzip2", BZ2_bzdopen
)
1249 APT_COMPRESS_INIT("xz", fdopen
)
1250 APT_COMPRESS_INIT("lzma", fdopen
)
1252 #undef APT_COMPRESS_INIT
1257 d
= new FileFdPrivate();
1259 d
->compressor
= compressor
;
1260 #if defined HAVE_ZLIB || defined HAVE_BZ2 || defined HAVE_LZMA
1261 if ((Flags
& AutoClose
) != AutoClose
&& compress_open
!= NULL
)
1263 // Need to duplicate fd here or gz/bz2 close for cleanup will close the fd as well
1264 int const internFd
= dup(iFd
);
1266 return FileFdErrno("OpenInternDescriptor", _("Could not open file descriptor %d"), iFd
);
1272 #if defined HAVE_ZLIB || defined HAVE_BZ2 || defined HAVE_LZMA
1273 if (compress_open
!= NULL
)
1275 void* compress_struct
= NULL
;
1276 if ((Mode
& ReadWrite
) == ReadWrite
)
1277 compress_struct
= compress_open(iFd
, "r+");
1278 else if ((Mode
& WriteOnly
) == WriteOnly
)
1279 compress_struct
= compress_open(iFd
, "w");
1281 compress_struct
= compress_open(iFd
, "r");
1282 if (compress_struct
== NULL
)
1286 /* dummy so that the rest can be 'else if's */;
1288 else if (compressor
.Name
== "gzip")
1289 d
->gz
= (gzFile
) compress_struct
;
1292 else if (compressor
.Name
== "bzip2")
1293 d
->bz2
= (BZFILE
*) compress_struct
;
1296 else if (compressor
.Name
== "xz" || compressor
.Name
== "lzma")
1298 uint32_t const xzlevel
= 6;
1299 uint64_t const memlimit
= UINT64_MAX
;
1300 if (d
->lzma
== NULL
)
1301 d
->lzma
= new FileFdPrivate::LZMAFILE
;
1302 d
->lzma
->file
= (FILE*) compress_struct
;
1303 lzma_stream tmp_stream
= LZMA_STREAM_INIT
;
1304 d
->lzma
->stream
= tmp_stream
;
1306 if ((Mode
& ReadWrite
) == ReadWrite
)
1307 return FileFdError("ReadWrite mode is not supported for file %s", FileName
.c_str());
1309 if ((Mode
& WriteOnly
) == WriteOnly
)
1311 if (compressor
.Name
== "xz")
1313 if (lzma_easy_encoder(&d
->lzma
->stream
, xzlevel
, LZMA_CHECK_CRC32
) != LZMA_OK
)
1318 lzma_options_lzma options
;
1319 lzma_lzma_preset(&options
, xzlevel
);
1320 if (lzma_alone_encoder(&d
->lzma
->stream
, &options
) != LZMA_OK
)
1323 d
->lzma
->compressing
= true;
1327 if (compressor
.Name
== "xz")
1329 if (lzma_auto_decoder(&d
->lzma
->stream
, memlimit
, 0) != LZMA_OK
)
1334 if (lzma_alone_decoder(&d
->lzma
->stream
, memlimit
) != LZMA_OK
)
1337 d
->lzma
->compressing
= false;
1341 Flags
|= Compressed
;
1346 // collect zombies here in case we reopen
1347 if (d
->compressor_pid
> 0)
1348 ExecWait(d
->compressor_pid
, "FileFdCompressor", true);
1350 if ((Mode
& ReadWrite
) == ReadWrite
)
1351 return FileFdError("ReadWrite mode is not supported for file %s", FileName
.c_str());
1353 bool const Comp
= (Mode
& WriteOnly
) == WriteOnly
;
1356 // Handle 'decompression' of empty files
1359 if (Buf
.st_size
== 0 && S_ISFIFO(Buf
.st_mode
) == false)
1362 // We don't need the file open - instead let the compressor open it
1363 // as he properly knows better how to efficiently read from 'his' file
1364 if (FileName
.empty() == false)
1371 // Create a data pipe
1372 int Pipe
[2] = {-1,-1};
1373 if (pipe(Pipe
) != 0)
1374 return FileFdErrno("pipe",_("Failed to create subprocess IPC"));
1375 for (int J
= 0; J
!= 2; J
++)
1376 SetCloseExec(Pipe
[J
],true);
1378 d
->compressed_fd
= iFd
;
1387 d
->compressor_pid
= ExecFork();
1388 if (d
->compressor_pid
== 0)
1392 dup2(d
->compressed_fd
,STDOUT_FILENO
);
1393 dup2(Pipe
[0],STDIN_FILENO
);
1397 if (d
->compressed_fd
!= -1)
1398 dup2(d
->compressed_fd
,STDIN_FILENO
);
1399 dup2(Pipe
[1],STDOUT_FILENO
);
1401 int const nullfd
= open("/dev/null", O_WRONLY
);
1404 dup2(nullfd
,STDERR_FILENO
);
1408 SetCloseExec(STDOUT_FILENO
,false);
1409 SetCloseExec(STDIN_FILENO
,false);
1411 std::vector
<char const*> Args
;
1412 Args
.push_back(compressor
.Binary
.c_str());
1413 std::vector
<std::string
> const * const addArgs
=
1414 (Comp
== true) ? &(compressor
.CompressArgs
) : &(compressor
.UncompressArgs
);
1415 for (std::vector
<std::string
>::const_iterator a
= addArgs
->begin();
1416 a
!= addArgs
->end(); ++a
)
1417 Args
.push_back(a
->c_str());
1418 if (Comp
== false && FileName
.empty() == false)
1420 // commands not needing arguments, do not need to be told about using standard output
1421 // in reality, only testcases with tools like cat, rev, rot13, … are able to trigger this
1422 if (compressor
.CompressArgs
.empty() == false && compressor
.UncompressArgs
.empty() == false)
1423 Args
.push_back("--stdout");
1424 if (TemporaryFileName
.empty() == false)
1425 Args
.push_back(TemporaryFileName
.c_str());
1427 Args
.push_back(FileName
.c_str());
1429 Args
.push_back(NULL
);
1431 execvp(Args
[0],(char **)&Args
[0]);
1432 cerr
<< _("Failed to exec compressor ") << Args
[0] << endl
;
1443 // FileFd::~File - Closes the file /*{{{*/
1444 // ---------------------------------------------------------------------
1445 /* If the proper modes are selected then we close the Fd and possibly
1446 unlink the file on error. */
1451 d
->CloseDown(FileName
);
1456 // FileFd::Read - Read a bit of the file /*{{{*/
1457 // ---------------------------------------------------------------------
1458 /* We are careful to handle interruption by a signal while reading
1460 bool FileFd::Read(void *To
,unsigned long long Size
,unsigned long long *Actual
)
1466 *((char *)To
) = '\0';
1470 /* dummy so that the rest can be 'else if's */;
1472 else if (d
!= NULL
&& d
->gz
!= NULL
)
1473 Res
= gzread(d
->gz
,To
,Size
);
1476 else if (d
!= NULL
&& d
->bz2
!= NULL
)
1477 Res
= BZ2_bzread(d
->bz2
,To
,Size
);
1480 else if (d
!= NULL
&& d
->lzma
!= NULL
)
1482 if (d
->lzma
->eof
== true)
1485 d
->lzma
->stream
.next_out
= (uint8_t *) To
;
1486 d
->lzma
->stream
.avail_out
= Size
;
1487 if (d
->lzma
->stream
.avail_in
== 0)
1489 d
->lzma
->stream
.next_in
= d
->lzma
->buffer
;
1490 d
->lzma
->stream
.avail_in
= fread(d
->lzma
->buffer
, 1, sizeof(d
->lzma
->buffer
)/sizeof(d
->lzma
->buffer
[0]), d
->lzma
->file
);
1492 d
->lzma
->err
= lzma_code(&d
->lzma
->stream
, LZMA_RUN
);
1493 if (d
->lzma
->err
== LZMA_STREAM_END
)
1495 d
->lzma
->eof
= true;
1496 Res
= Size
- d
->lzma
->stream
.avail_out
;
1498 else if (d
->lzma
->err
!= LZMA_OK
)
1505 Res
= Size
- d
->lzma
->stream
.avail_out
;
1508 // lzma run was okay, but produced no output…
1516 Res
= read(iFd
,To
,Size
);
1522 // trick the while-loop into running again
1528 /* dummy so that the rest can be 'else if's */;
1530 else if (d
!= NULL
&& d
->gz
!= NULL
)
1533 char const * const errmsg
= gzerror(d
->gz
, &err
);
1535 return FileFdError("gzread: %s (%d: %s)", _("Read error"), err
, errmsg
);
1539 else if (d
!= NULL
&& d
->bz2
!= NULL
)
1542 char const * const errmsg
= BZ2_bzerror(d
->bz2
, &err
);
1543 if (err
!= BZ_IO_ERROR
)
1544 return FileFdError("BZ2_bzread: %s %s (%d: %s)", FileName
.c_str(), _("Read error"), err
, errmsg
);
1548 else if (d
!= NULL
&& d
->lzma
!= NULL
)
1549 return FileFdError("lzma_read: %s (%d)", _("Read error"), d
->lzma
->err
);
1551 return FileFdErrno("read",_("Read error"));
1554 To
= (char *)To
+ Res
;
1561 while (Res
> 0 && Size
> 0);
1573 return FileFdError(_("read, still have %llu to read but none left"), Size
);
1576 // FileFd::ReadLine - Read a complete line from the file /*{{{*/
1577 // ---------------------------------------------------------------------
1578 /* Beware: This method can be quiet slow for big buffers on UNcompressed
1579 files because of the naive implementation! */
1580 char* FileFd::ReadLine(char *To
, unsigned long long const Size
)
1584 if (d
!= NULL
&& d
->gz
!= NULL
)
1585 return gzgets(d
->gz
, To
, Size
);
1588 unsigned long long read
= 0;
1589 while ((Size
- 1) != read
)
1591 unsigned long long done
= 0;
1592 if (Read(To
+ read
, 1, &done
) == false)
1596 if (To
[read
++] == '\n')
1605 // FileFd::Write - Write to the file /*{{{*/
1606 // ---------------------------------------------------------------------
1608 bool FileFd::Write(const void *From
,unsigned long long Size
)
1615 /* dummy so that the rest can be 'else if's */;
1617 else if (d
!= NULL
&& d
->gz
!= NULL
)
1618 Res
= gzwrite(d
->gz
,From
,Size
);
1621 else if (d
!= NULL
&& d
->bz2
!= NULL
)
1622 Res
= BZ2_bzwrite(d
->bz2
,(void*)From
,Size
);
1625 else if (d
!= NULL
&& d
->lzma
!= NULL
)
1627 d
->lzma
->stream
.next_in
= (uint8_t *)From
;
1628 d
->lzma
->stream
.avail_in
= Size
;
1629 d
->lzma
->stream
.next_out
= d
->lzma
->buffer
;
1630 d
->lzma
->stream
.avail_out
= sizeof(d
->lzma
->buffer
)/sizeof(d
->lzma
->buffer
[0]);
1631 d
->lzma
->err
= lzma_code(&d
->lzma
->stream
, LZMA_RUN
);
1632 if (d
->lzma
->err
!= LZMA_OK
)
1634 size_t const n
= sizeof(d
->lzma
->buffer
)/sizeof(d
->lzma
->buffer
[0]) - d
->lzma
->stream
.avail_out
;
1635 size_t const m
= (n
== 0) ? 0 : fwrite(d
->lzma
->buffer
, 1, n
, d
->lzma
->file
);
1639 Res
= Size
- d
->lzma
->stream
.avail_in
;
1643 Res
= write(iFd
,From
,Size
);
1645 if (Res
< 0 && errno
== EINTR
)
1650 /* dummy so that the rest can be 'else if's */;
1652 else if (d
!= NULL
&& d
->gz
!= NULL
)
1655 char const * const errmsg
= gzerror(d
->gz
, &err
);
1657 return FileFdError("gzwrite: %s (%d: %s)", _("Write error"), err
, errmsg
);
1661 else if (d
!= NULL
&& d
->bz2
!= NULL
)
1664 char const * const errmsg
= BZ2_bzerror(d
->bz2
, &err
);
1665 if (err
!= BZ_IO_ERROR
)
1666 return FileFdError("BZ2_bzwrite: %s (%d: %s)", _("Write error"), err
, errmsg
);
1670 else if (d
!= NULL
&& d
->lzma
!= NULL
)
1671 return FileFdErrno("lzma_fwrite", _("Write error"));
1673 return FileFdErrno("write",_("Write error"));
1676 From
= (char const *)From
+ Res
;
1681 while (Res
> 0 && Size
> 0);
1686 return FileFdError(_("write, still have %llu to write but couldn't"), Size
);
1688 bool FileFd::Write(int Fd
, const void *From
, unsigned long long Size
)
1694 Res
= write(Fd
,From
,Size
);
1695 if (Res
< 0 && errno
== EINTR
)
1698 return _error
->Errno("write",_("Write error"));
1700 From
= (char const *)From
+ Res
;
1703 while (Res
> 0 && Size
> 0);
1708 return _error
->Error(_("write, still have %llu to write but couldn't"), Size
);
1711 // FileFd::Seek - Seek in the file /*{{{*/
1712 // ---------------------------------------------------------------------
1714 bool FileFd::Seek(unsigned long long To
)
1718 if (d
!= NULL
&& (d
->pipe
== true || d
->InternalStream() == true))
1720 // Our poor man seeking in pipes is costly, so try to avoid it
1721 unsigned long long seekpos
= Tell();
1724 else if (seekpos
< To
)
1725 return Skip(To
- seekpos
);
1727 if ((d
->openmode
& ReadOnly
) != ReadOnly
)
1728 return FileFdError("Reopen is only implemented for read-only files!");
1729 d
->InternalClose(FileName
);
1733 if (TemporaryFileName
.empty() == false)
1734 iFd
= open(TemporaryFileName
.c_str(), O_RDONLY
);
1735 else if (FileName
.empty() == false)
1736 iFd
= open(FileName
.c_str(), O_RDONLY
);
1739 if (d
->compressed_fd
> 0)
1740 if (lseek(d
->compressed_fd
, 0, SEEK_SET
) != 0)
1741 iFd
= d
->compressed_fd
;
1743 return FileFdError("Reopen is not implemented for pipes opened with FileFd::OpenDescriptor()!");
1746 if (OpenInternDescriptor(d
->openmode
, d
->compressor
) == false)
1747 return FileFdError("Seek on file %s because it couldn't be reopened", FileName
.c_str());
1757 if (d
!= NULL
&& d
->gz
)
1758 res
= gzseek(d
->gz
,To
,SEEK_SET
);
1761 res
= lseek(iFd
,To
,SEEK_SET
);
1762 if (res
!= (off_t
)To
)
1763 return FileFdError("Unable to seek to %llu", To
);
1770 // FileFd::Skip - Seek in the file /*{{{*/
1771 // ---------------------------------------------------------------------
1773 bool FileFd::Skip(unsigned long long Over
)
1775 if (d
!= NULL
&& (d
->pipe
== true || d
->InternalStream() == true))
1780 unsigned long long toread
= std::min((unsigned long long) sizeof(buffer
), Over
);
1781 if (Read(buffer
, toread
) == false)
1782 return FileFdError("Unable to seek ahead %llu",Over
);
1790 if (d
!= NULL
&& d
->gz
!= NULL
)
1791 res
= gzseek(d
->gz
,Over
,SEEK_CUR
);
1794 res
= lseek(iFd
,Over
,SEEK_CUR
);
1796 return FileFdError("Unable to seek ahead %llu",Over
);
1803 // FileFd::Truncate - Truncate the file /*{{{*/
1804 // ---------------------------------------------------------------------
1806 bool FileFd::Truncate(unsigned long long To
)
1808 // truncating /dev/null is always successful - as we get an error otherwise
1809 if (To
== 0 && FileName
== "/dev/null")
1811 #if defined HAVE_ZLIB || defined HAVE_BZ2 || defined HAVE_LZMA
1812 if (d
!= NULL
&& (d
->InternalStream() == true
1817 return FileFdError("Truncating compressed files is not implemented (%s)", FileName
.c_str());
1819 if (ftruncate(iFd
,To
) != 0)
1820 return FileFdError("Unable to truncate to %llu",To
);
1825 // FileFd::Tell - Current seek position /*{{{*/
1826 // ---------------------------------------------------------------------
1828 unsigned long long FileFd::Tell()
1830 // In theory, we could just return seekpos here always instead of
1831 // seeking around, but not all users of FileFd use always Seek() and co
1832 // so d->seekpos isn't always true and we can just use it as a hint if
1833 // we have nothing else, but not always as an authority…
1834 if (d
!= NULL
&& (d
->pipe
== true || d
->InternalStream() == true))
1839 if (d
!= NULL
&& d
->gz
!= NULL
)
1840 Res
= gztell(d
->gz
);
1843 Res
= lseek(iFd
,0,SEEK_CUR
);
1844 if (Res
== (off_t
)-1)
1845 FileFdErrno("lseek","Failed to determine the current file position");
1851 static bool StatFileFd(char const * const msg
, int const iFd
, std::string
const &FileName
, struct stat
&Buf
, FileFdPrivate
* const d
) /*{{{*/
1853 bool ispipe
= (d
!= NULL
&& d
->pipe
== true);
1854 if (ispipe
== false)
1856 if (fstat(iFd
,&Buf
) != 0)
1857 // higher-level code will generate more meaningful messages,
1858 // even translated this would be meaningless for users
1859 return _error
->Errno("fstat", "Unable to determine %s for fd %i", msg
, iFd
);
1860 if (FileName
.empty() == false)
1861 ispipe
= S_ISFIFO(Buf
.st_mode
);
1864 // for compressor pipes st_size is undefined and at 'best' zero
1867 // we set it here, too, as we get the info here for free
1868 // in theory the Open-methods should take care of it already
1871 if (stat(FileName
.c_str(), &Buf
) != 0)
1872 return _error
->Errno("fstat", "Unable to determine %s for file %s", msg
, FileName
.c_str());
1877 // FileFd::FileSize - Return the size of the file /*{{{*/
1878 unsigned long long FileFd::FileSize()
1881 if (StatFileFd("file size", iFd
, FileName
, Buf
, d
) == false)
1889 // FileFd::ModificationTime - Return the time of last touch /*{{{*/
1890 time_t FileFd::ModificationTime()
1893 if (StatFileFd("modification time", iFd
, FileName
, Buf
, d
) == false)
1898 return Buf
.st_mtime
;
1901 // FileFd::Size - Return the size of the content in the file /*{{{*/
1902 // ---------------------------------------------------------------------
1904 unsigned long long FileFd::Size()
1906 unsigned long long size
= FileSize();
1908 // for compressor pipes st_size is undefined and at 'best' zero,
1909 // so we 'read' the content and 'seek' back - see there
1910 if (d
!= NULL
&& (d
->pipe
== true || (d
->InternalStream() == true && size
> 0)))
1912 unsigned long long const oldSeek
= Tell();
1914 unsigned long long read
= 0;
1916 if (Read(ignore
, sizeof(ignore
), &read
) == false)
1926 // only check gzsize if we are actually a gzip file, just checking for
1927 // "gz" is not sufficient as uncompressed files could be opened with
1928 // gzopen in "direct" mode as well
1929 else if (d
!= NULL
&& d
->gz
&& !gzdirect(d
->gz
) && size
> 0)
1931 off_t
const oldPos
= lseek(iFd
,0,SEEK_CUR
);
1932 /* unfortunately zlib.h doesn't provide a gzsize(), so we have to do
1933 * this ourselves; the original (uncompressed) file size is the last 32
1934 * bits of the file */
1935 // FIXME: Size for gz-files is limited by 32bit… no largefile support
1936 if (lseek(iFd
, -4, SEEK_END
) < 0)
1938 FileFdErrno("lseek","Unable to seek to end of gzipped file");
1942 if (read(iFd
, &size
, 4) != 4)
1944 FileFdErrno("read","Unable to read original size of gzipped file");
1947 size
= le32toh(size
);
1949 if (lseek(iFd
, oldPos
, SEEK_SET
) < 0)
1951 FileFdErrno("lseek","Unable to seek in gzipped file");
1962 // FileFd::Close - Close the file if the close flag is set /*{{{*/
1963 // ---------------------------------------------------------------------
1965 bool FileFd::Close()
1971 if ((Flags
& AutoClose
) == AutoClose
)
1973 if ((Flags
& Compressed
) != Compressed
&& iFd
> 0 && close(iFd
) != 0)
1974 Res
&= _error
->Errno("close",_("Problem closing the file %s"), FileName
.c_str());
1977 Res
&= d
->CloseDown(FileName
);
1983 if ((Flags
& Replace
) == Replace
) {
1984 if (rename(TemporaryFileName
.c_str(), FileName
.c_str()) != 0)
1985 Res
&= _error
->Errno("rename",_("Problem renaming the file %s to %s"), TemporaryFileName
.c_str(), FileName
.c_str());
1987 FileName
= TemporaryFileName
; // for the unlink() below.
1988 TemporaryFileName
.clear();
1993 if ((Flags
& Fail
) == Fail
&& (Flags
& DelOnFail
) == DelOnFail
&&
1994 FileName
.empty() == false)
1995 if (unlink(FileName
.c_str()) != 0)
1996 Res
&= _error
->WarningE("unlnk",_("Problem unlinking the file %s"), FileName
.c_str());
2003 // FileFd::Sync - Sync the file /*{{{*/
2004 // ---------------------------------------------------------------------
2008 if (fsync(iFd
) != 0)
2009 return FileFdErrno("sync",_("Problem syncing the file"));
2013 // FileFd::FileFdErrno - set Fail and call _error->Errno *{{{*/
2014 bool FileFd::FileFdErrno(const char *Function
, const char *Description
,...)
2018 size_t msgSize
= 400;
2019 int const errsv
= errno
;
2022 va_start(args
,Description
);
2023 if (_error
->InsertErrno(GlobalError::ERROR
, Function
, Description
, args
, errsv
, msgSize
) == false)
2030 // FileFd::FileFdError - set Fail and call _error->Error *{{{*/
2031 bool FileFd::FileFdError(const char *Description
,...) {
2034 size_t msgSize
= 400;
2037 va_start(args
,Description
);
2038 if (_error
->Insert(GlobalError::ERROR
, Description
, args
, msgSize
) == false)
2046 APT_DEPRECATED gzFile
FileFd::gzFd() {
2054 // Glob - wrapper around "glob()" /*{{{*/
2055 std::vector
<std::string
> Glob(std::string
const &pattern
, int flags
)
2057 std::vector
<std::string
> result
;
2062 glob_res
= glob(pattern
.c_str(), flags
, NULL
, &globbuf
);
2066 if(glob_res
!= GLOB_NOMATCH
) {
2067 _error
->Errno("glob", "Problem with glob");
2073 for(i
=0;i
<globbuf
.gl_pathc
;i
++)
2074 result
.push_back(string(globbuf
.gl_pathv
[i
]));
2080 std::string
GetTempDir() /*{{{*/
2082 const char *tmpdir
= getenv("TMPDIR");
2090 if (!tmpdir
|| strlen(tmpdir
) == 0 || // tmpdir is set
2091 stat(tmpdir
, &st
) != 0 || (st
.st_mode
& S_IFDIR
) == 0 || // exists and is directory
2092 access(tmpdir
, R_OK
| W_OK
| X_OK
) != 0 // current user has rwx access to directory
2096 return string(tmpdir
);
2099 FileFd
* GetTempFile(std::string
const &Prefix
, bool ImmediateUnlink
) /*{{{*/
2102 FileFd
*Fd
= new FileFd();
2104 std::string tempdir
= GetTempDir();
2105 snprintf(fn
, sizeof(fn
), "%s/%s.XXXXXX",
2106 tempdir
.c_str(), Prefix
.c_str());
2107 int fd
= mkstemp(fn
);
2112 _error
->Errno("GetTempFile",_("Unable to mkstemp %s"), fn
);
2115 if (!Fd
->OpenDescriptor(fd
, FileFd::WriteOnly
, FileFd::None
, true))
2117 _error
->Errno("GetTempFile",_("Unable to write to %s"),fn
);
2124 bool Rename(std::string From
, std::string To
) /*{{{*/
2126 if (rename(From
.c_str(),To
.c_str()) != 0)
2128 _error
->Error(_("rename failed, %s (%s -> %s)."),strerror(errno
),
2129 From
.c_str(),To
.c_str());
2135 bool Popen(const char* Args
[], FileFd
&Fd
, pid_t
&Child
, FileFd::OpenMode Mode
)/*{{{*/
2138 if (Mode
!= FileFd::ReadOnly
&& Mode
!= FileFd::WriteOnly
)
2139 return _error
->Error("Popen supports ReadOnly (x)or WriteOnly mode only");
2141 int Pipe
[2] = {-1, -1};
2143 return _error
->Errno("pipe", _("Failed to create subprocess IPC"));
2145 std::set
<int> keep_fds
;
2146 keep_fds
.insert(Pipe
[0]);
2147 keep_fds
.insert(Pipe
[1]);
2148 Child
= ExecFork(keep_fds
);
2150 return _error
->Errno("fork", "Failed to fork");
2153 if(Mode
== FileFd::ReadOnly
)
2158 else if(Mode
== FileFd::WriteOnly
)
2164 if(Mode
== FileFd::ReadOnly
)
2168 } else if(Mode
== FileFd::WriteOnly
)
2171 execv(Args
[0], (char**)Args
);
2174 if(Mode
== FileFd::ReadOnly
)
2178 } else if(Mode
== FileFd::WriteOnly
)
2183 Fd
.OpenDescriptor(fd
, Mode
, FileFd::None
, true);
2188 bool DropPrivileges() /*{{{*/
2190 if(_config
->FindB("Debug::NoDropPrivs", false) == true)
2194 #if defined(PR_SET_NO_NEW_PRIVS) && ( PR_SET_NO_NEW_PRIVS != 38 )
2195 #error "PR_SET_NO_NEW_PRIVS is defined, but with a different value than expected!"
2197 // see prctl(2), needs linux3.5 at runtime - magic constant to avoid it at buildtime
2198 int ret
= prctl(38, 1, 0, 0, 0);
2199 // ignore EINVAL - kernel is too old to understand the option
2200 if(ret
< 0 && errno
!= EINVAL
)
2201 _error
->Warning("PR_SET_NO_NEW_PRIVS failed with %i", ret
);
2204 // empty setting disables privilege dropping - this also ensures
2205 // backward compatibility, see bug #764506
2206 const std::string toUser
= _config
->Find("APT::Sandbox::User");
2210 // uid will be 0 in the end, but gid might be different anyway
2211 uid_t
const old_uid
= getuid();
2212 gid_t
const old_gid
= getgid();
2217 struct passwd
*pw
= getpwnam(toUser
.c_str());
2219 return _error
->Error("No user %s, can not drop rights", toUser
.c_str());
2221 // Do not change the order here, it might break things
2222 if (setgroups(1, &pw
->pw_gid
))
2223 return _error
->Errno("setgroups", "Failed to setgroups");
2225 if (setegid(pw
->pw_gid
) != 0)
2226 return _error
->Errno("setegid", "Failed to setegid");
2228 if (setgid(pw
->pw_gid
) != 0)
2229 return _error
->Errno("setgid", "Failed to setgid");
2231 if (setuid(pw
->pw_uid
) != 0)
2232 return _error
->Errno("setuid", "Failed to setuid");
2234 // the seteuid() is probably uneeded (at least thats what the linux
2235 // man-page says about setuid(2)) but we cargo culted it anyway
2236 if (seteuid(pw
->pw_uid
) != 0)
2237 return _error
->Errno("seteuid", "Failed to seteuid");
2239 // Verify that the user has only a single group, and the correct one
2241 if (getgroups(1, groups
) != 1)
2242 return _error
->Errno("getgroups", "Could not get new groups");
2243 if (groups
[0] != pw
->pw_gid
)
2244 return _error
->Error("Could not switch group");
2246 // Verify that gid, egid, uid, and euid changed
2247 if (getgid() != pw
->pw_gid
)
2248 return _error
->Error("Could not switch group");
2249 if (getegid() != pw
->pw_gid
)
2250 return _error
->Error("Could not switch effective group");
2251 if (getuid() != pw
->pw_uid
)
2252 return _error
->Error("Could not switch user");
2253 if (geteuid() != pw
->pw_uid
)
2254 return _error
->Error("Could not switch effective user");
2256 #ifdef HAVE_GETRESUID
2257 // verify that the saved set-user-id was changed as well
2261 if (getresuid(&ruid
, &euid
, &suid
))
2262 return _error
->Errno("getresuid", "Could not get saved set-user-ID");
2263 if (suid
!= pw
->pw_uid
)
2264 return _error
->Error("Could not switch saved set-user-ID");
2267 #ifdef HAVE_GETRESGID
2268 // verify that the saved set-group-id was changed as well
2272 if (getresgid(&rgid
, &egid
, &sgid
))
2273 return _error
->Errno("getresuid", "Could not get saved set-group-ID");
2274 if (sgid
!= pw
->pw_gid
)
2275 return _error
->Error("Could not switch saved set-group-ID");
2278 // Check that uid and gid changes do not work anymore
2279 if (pw
->pw_gid
!= old_gid
&& (setgid(old_gid
) != -1 || setegid(old_gid
) != -1))
2280 return _error
->Error("Could restore a gid to root, privilege dropping did not work");
2282 if (pw
->pw_uid
!= old_uid
&& (setuid(old_uid
) != -1 || seteuid(old_uid
) != -1))
2283 return _error
->Error("Could restore a uid to root, privilege dropping did not work");